Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class BoolEPStat : public EntryPointStat {

public:
explicit BoolEPStat(llvm::StringLiteral Name);
unsigned value() const { return Value && *Value; }
std::optional<bool> value() const { return Value; }
void set(bool V) {
assert(!Value.has_value());
Value = V;
Expand Down Expand Up @@ -98,7 +98,7 @@ class UnsignedEPStat : public EntryPointStat {

public:
explicit UnsignedEPStat(llvm::StringLiteral Name);
unsigned value() const { return Value.value_or(0); }
std::optional<unsigned> value() const { return Value; }
void reset() { Value.reset(); }
void set(unsigned V) {
assert(!Value.has_value());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class FunctionSummariesTy {
/// The number of times the function has been inlined.
unsigned TimesInlined : 32;

/// Running time for syntax-based AST analysis in milliseconds.
std::optional<unsigned> SyntaxRunningTime = std::nullopt;

FunctionSummary()
: TotalBasicBlocks(0), InlineChecked(0), MayInline(0),
TimesInlined(0) {}
Expand All @@ -69,6 +72,11 @@ class FunctionSummariesTy {
return I;
}

FunctionSummary const *findSummary(const Decl *D) const {
auto I = Map.find(D);
return I == Map.end() ? nullptr : &I->second;
}

void markMayInline(const Decl *D) {
MapTy::iterator I = findOrInsertSummary(D);
I->second.InlineChecked = 1;
Expand Down
62 changes: 47 additions & 15 deletions clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ struct Registry {

struct Snapshot {
const Decl *EntryPoint;
std::vector<bool> BoolStatValues;
// Boolean statistics are always set explicitly. If they are not set, their
// value is absent resulting in empty CSV cells
std::vector<std::optional<bool>> BoolStatValues;
Comment on lines -36 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this change (where you modify the behavior of the boolean statistics) related to the rest of this commit (which handles unsigned statistics)? Does this influence anything (I don't see a corresponding change in the test)?

(By the way, does upstream clang have any boolean statistics at all?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I will break it down into a few PRs. here is the first one: #162817

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

// Explicitly set statistics may not have a value set, so they are separate
// from other unsigned statistics
std::vector<std::optional<unsigned>> UnsignedExplicitlySetStatValues;
// These are counting and maximizing statistics that initialize to 0, which
// is meaningful even if they are never updated, so their value is always
// present.
std::vector<unsigned> UnsignedStatValues;

void dumpAsCSV(llvm::raw_ostream &OS) const;
Expand All @@ -48,10 +56,14 @@ static llvm::ManagedStatic<Registry> StatsRegistry;

namespace {
template <typename Callback> void enumerateStatVectors(const Callback &Fn) {
// This order is important, it matches the order of Snapshot 3 fields:
// - BoolStatValues
Fn(StatsRegistry->BoolStats);
// - UnsignedExplicitlySetStatValues
Fn(StatsRegistry->UnsignedStats);
// - UnsignedStatValues
Fn(StatsRegistry->CounterStats);
Fn(StatsRegistry->UnsignedMaxStats);
Fn(StatsRegistry->UnsignedStats);
}
} // namespace

Expand Down Expand Up @@ -120,11 +132,21 @@ UnsignedEPStat::UnsignedEPStat(llvm::StringLiteral Name)
StatsRegistry->UnsignedStats.push_back(this);
}

static std::vector<std::optional<unsigned>>
consumeUnsignedExplicitlySetStats() {
std::vector<std::optional<unsigned>> Result;
Result.reserve(StatsRegistry->UnsignedStats.size());
for (auto *M : StatsRegistry->UnsignedStats) {
Result.push_back(M->value());
M->reset();
}
return Result;
}

static std::vector<unsigned> consumeUnsignedStats() {
std::vector<unsigned> Result;
Result.reserve(StatsRegistry->CounterStats.size() +
StatsRegistry->UnsignedMaxStats.size() +
StatsRegistry->UnsignedStats.size());
StatsRegistry->UnsignedMaxStats.size());
for (auto *M : StatsRegistry->CounterStats) {
Result.push_back(M->value());
M->reset();
Expand All @@ -133,10 +155,6 @@ static std::vector<unsigned> consumeUnsignedStats() {
Result.push_back(M->value());
M->reset();
}
for (auto *M : StatsRegistry->UnsignedStats) {
Result.push_back(M->value());
M->reset();
}
return Result;
}

Expand All @@ -159,21 +177,33 @@ static std::string getUSR(const Decl *D) {
}

void Registry::Snapshot::dumpAsCSV(llvm::raw_ostream &OS) const {
auto printAsBoolOpt = [&OS](std::optional<bool> B) {
OS << (B.has_value() ? (*B ? "true" : "false") : "");
};
auto printAsUnsignOpt = [&OS](std::optional<unsigned> U) {
OS << (U.has_value() ? std::to_string(*U) : "");
};
auto commaIfNeeded = [&OS](const auto &Vec1, const auto &Vec2) {
if (!Vec1.empty() && !Vec2.empty())
OS << ",";
};

OS << '"';
llvm::printEscapedString(getUSR(EntryPoint), OS);
OS << "\",\"";
OS << StatsRegistry->EscapedCPPFileName << "\",\"";
llvm::printEscapedString(
clang::AnalysisDeclContext::getFunctionName(EntryPoint), OS);
OS << "\",";
auto PrintAsBool = [&OS](bool B) { OS << (B ? "true" : "false"); };
llvm::interleave(BoolStatValues, OS, PrintAsBool, ",");
OS << ((BoolStatValues.empty() || UnsignedStatValues.empty()) ? "" : ",");
llvm::interleave(BoolStatValues, OS, printAsBoolOpt, ",");
commaIfNeeded(BoolStatValues, UnsignedExplicitlySetStatValues);
llvm::interleave(UnsignedExplicitlySetStatValues, OS, printAsUnsignOpt, ",");
commaIfNeeded(UnsignedExplicitlySetStatValues, UnsignedStatValues);
llvm::interleave(UnsignedStatValues, OS, [&OS](unsigned U) { OS << U; }, ",");
}

static std::vector<bool> consumeBoolStats() {
std::vector<bool> Result;
static std::vector<std::optional<bool>> consumeBoolStats() {
std::vector<std::optional<bool>> Result;
Result.reserve(StatsRegistry->BoolStats.size());
for (auto *M : StatsRegistry->BoolStats) {
Result.push_back(M->value());
Expand All @@ -184,9 +214,11 @@ static std::vector<bool> consumeBoolStats() {

void EntryPointStat::takeSnapshot(const Decl *EntryPoint) {
auto BoolValues = consumeBoolStats();
auto UnsignedExplicitlySetValues = consumeUnsignedExplicitlySetStats();
auto UnsignedValues = consumeUnsignedStats();
StatsRegistry->Snapshots.push_back(
{EntryPoint, std::move(BoolValues), std::move(UnsignedValues)});
StatsRegistry->Snapshots.push_back({EntryPoint, std::move(BoolValues),
std::move(UnsignedExplicitlySetValues),
std::move(UnsignedValues)});
}

void EntryPointStat::dumpStatsAsCSV(llvm::StringRef FileName) {
Expand Down
48 changes: 47 additions & 1 deletion clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <cmath>
#include <memory>
#include <utility>

Expand Down Expand Up @@ -142,7 +143,7 @@ class AnalysisConsumer : public AnalysisASTConsumer,
DigestAnalyzerOptions();

if (Opts.AnalyzerDisplayProgress || Opts.PrintStats ||
Opts.ShouldSerializeStats) {
Opts.ShouldSerializeStats || !Opts.DumpEntryPointStatsToCSV.empty()) {
AnalyzerTimers = std::make_unique<llvm::TimerGroup>(
"analyzer", "Analyzer timers");
SyntaxCheckTimer = std::make_unique<llvm::Timer>(
Expand Down Expand Up @@ -706,6 +707,7 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
}

static UnsignedEPStat PathRunningTime("PathRunningTime");
static UnsignedEPStat SyntaxRunningTime("SyntaxRunningTime");

void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
ExprEngine::InliningModes IMode,
Expand Down Expand Up @@ -744,6 +746,8 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
SyntaxCheckTimer->stopTimer();
llvm::TimeRecord CheckerEndTime = SyntaxCheckTimer->getTotalTime();
CheckerEndTime -= CheckerStartTime;
FunctionSummaries.findOrInsertSummary(D)->second.SyntaxRunningTime =
std::lround(CheckerEndTime.getWallTime() * 1000);
DisplayTime(CheckerEndTime);
}
}
Expand All @@ -758,6 +762,36 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
}
}

namespace {
template <typename DeclT>
static clang::Decl *preferDefinitionImpl(clang::Decl *D) {
if (auto *X = dyn_cast<DeclT>(D))
if (auto *Def = X->getDefinition())
return Def;
return D;
}

template <> clang::Decl *preferDefinitionImpl<ObjCMethodDecl>(clang::Decl *D) {
if (const auto *X = dyn_cast<ObjCMethodDecl>(D)) {
for (auto *I : X->redecls())
if (I->hasBody())
return I;
}
return D;
}

static Decl *getDefinitionOrCanonicalDecl(Decl *D) {
assert(D);
D = D->getCanonicalDecl();
D = preferDefinitionImpl<VarDecl>(D);
D = preferDefinitionImpl<FunctionDecl>(D);
D = preferDefinitionImpl<TagDecl>(D);
D = preferDefinitionImpl<ObjCMethodDecl>(D);
assert(D);
return D;
}
} // namespace

//===----------------------------------------------------------------------===//
// Path-sensitive checking.
//===----------------------------------------------------------------------===//
Expand All @@ -774,6 +808,16 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
return;

const Decl *DefDecl = getDefinitionOrCanonicalDecl(D);

// Get the SyntaxRunningTime from the function summary, because it is computed
// during the AM_Syntax analysis, which is done at a different point in time
// and in different order, but always before AM_Path.
if (const auto *Summary = FunctionSummaries.findSummary(DefDecl);
Summary && Summary->SyntaxRunningTime.has_value()) {
SyntaxRunningTime.set(*Summary->SyntaxRunningTime);
}

ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode);

// Execute the worklist algorithm.
Expand All @@ -788,6 +832,8 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
ExprEngineTimer->stopTimer();
llvm::TimeRecord ExprEngineEndTime = ExprEngineTimer->getTotalTime();
ExprEngineEndTime -= ExprEngineStartTime;
PathRunningTime.set(static_cast<unsigned>(
std::lround(ExprEngineEndTime.getWallTime() * 1000)));
DisplayTime(ExprEngineEndTime);
}

Expand Down
10 changes: 6 additions & 4 deletions clang/test/Analysis/analyzer-stats/entry-point-stats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
// CHECK-NEXT: "c:@F@fib#i#": {
// CHECK-NEXT: "File": "{{.*}}entry-point-stats.cpp",
// CHECK-NEXT: "DebugName": "fib(unsigned int)",
// CHECK-NEXT: "PathRunningTime": "{{[0-9]+}}",
// CHECK-NEXT: "SyntaxRunningTime": "{{[0-9]+}}",
// CHECK-NEXT: "NumBlocks": "{{[0-9]+}}",
// CHECK-NEXT: "NumBlocksUnreachable": "{{[0-9]+}}",
// CHECK-NEXT: "NumCTUSteps": "{{[0-9]+}}",
Expand Down Expand Up @@ -39,12 +41,13 @@
// CHECK-NEXT: "MaxQueueSize": "{{[0-9]+}}",
// CHECK-NEXT: "MaxReachableSize": "{{[0-9]+}}",
// CHECK-NEXT: "MaxTimeSpentSolvingZ3Queries": "{{[0-9]+}}",
// CHECK-NEXT: "MaxValidBugClassSize": "{{[0-9]+}}",
// CHECK-NEXT: "PathRunningTime": "{{[0-9]+}}"
// CHECK-NEXT: "MaxValidBugClassSize": "{{[0-9]+}}"
// CHECK-NEXT: },
// CHECK-NEXT: "c:@F@main#I#**C#": {
// CHECK-NEXT: "File": "{{.*}}entry-point-stats.cpp",
// CHECK-NEXT: "DebugName": "main(int, char **)",
// CHECK-NEXT: "PathRunningTime": "{{[0-9]+}}",
// CHECK-NEXT: "SyntaxRunningTime": "{{[0-9]+}}",
// CHECK-NEXT: "NumBlocks": "{{[0-9]+}}",
// CHECK-NEXT: "NumBlocksUnreachable": "{{[0-9]+}}",
// CHECK-NEXT: "NumCTUSteps": "{{[0-9]+}}",
Expand Down Expand Up @@ -76,8 +79,7 @@
// CHECK-NEXT: "MaxQueueSize": "{{[0-9]+}}",
// CHECK-NEXT: "MaxReachableSize": "{{[0-9]+}}",
// CHECK-NEXT: "MaxTimeSpentSolvingZ3Queries": "{{[0-9]+}}",
// CHECK-NEXT: "MaxValidBugClassSize": "{{[0-9]+}}",
// CHECK-NEXT: "PathRunningTime": "{{[0-9]+}}"
// CHECK-NEXT: "MaxValidBugClassSize": "{{[0-9]+}}"
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NOT: non_entry_point
Expand Down