-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[clang][analyzer] Save path- and syntax-analysis times per entry point #162089
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
Conversation
Register and dump per-entry-point time taken by the path-sensitive and syntax-only analyses stages in milliseconds (SyntaxRunningTime, PathRunningTime). Also introduce a difference between unset stats and 0 for statistics categories for which it makes sense. So now if the syntax analysis does not run for a certain entry point, it's SyntaxRunningTime will have an empty value and not 0. This patch enables the timers if DumpEntryPointStatsToCSV is set, because in most cases you dump these stats to get a detailed view on analyzer performance. -- CPP-7097
@llvm/pr-subscribers-clang-static-analyzer-1 Author: Arseniy Zaostrovnykh (necto) ChangesRegister and dump per-entry-point time taken by the path-sensitive and syntax-only analyses stages in milliseconds (SyntaxRunningTime, PathRunningTime). Also introduce a difference between unset stats and 0 for statistics categories for which it makes sense. So now if the syntax analysis does not run for a certain entry point, it's SyntaxRunningTime will have an empty value and not 0. This patch enables the timers if DumpEntryPointStatsToCSV is set, because in most cases you dump these stats to get a detailed view on analyzer performance. -- CPP-7097 Full diff: https://github.com/llvm/llvm-project/pull/162089.diff 5 Files Affected:
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h
index 448e40269ca2d..d02f77194ad8c 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h
@@ -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;
@@ -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());
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
index 761395260a0cf..db4aec7c84754 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
@@ -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) {}
@@ -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;
diff --git a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
index 62ae62f2f2154..33c5df8132beb 100644
--- a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
+++ b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
@@ -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;
+ // 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;
@@ -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
@@ -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();
@@ -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;
}
@@ -159,6 +177,17 @@ 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 << "\",\"";
@@ -166,14 +195,15 @@ void Registry::Snapshot::dumpAsCSV(llvm::raw_ostream &OS) const {
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());
@@ -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) {
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index cf01e2f37c662..a5efa901b0714 100644
--- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -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>
@@ -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>(
@@ -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,
@@ -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);
}
}
@@ -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.
//===----------------------------------------------------------------------===//
@@ -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.
@@ -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);
}
diff --git a/clang/test/Analysis/analyzer-stats/entry-point-stats.cpp b/clang/test/Analysis/analyzer-stats/entry-point-stats.cpp
index 9cbe04550a8d3..b9099c6248108 100644
--- a/clang/test/Analysis/analyzer-stats/entry-point-stats.cpp
+++ b/clang/test/Analysis/analyzer-stats/entry-point-stats.cpp
@@ -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]+}}",
@@ -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]+}}",
@@ -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
|
@llvm/pr-subscribers-clang Author: Arseniy Zaostrovnykh (necto) ChangesRegister and dump per-entry-point time taken by the path-sensitive and syntax-only analyses stages in milliseconds (SyntaxRunningTime, PathRunningTime). Also introduce a difference between unset stats and 0 for statistics categories for which it makes sense. So now if the syntax analysis does not run for a certain entry point, it's SyntaxRunningTime will have an empty value and not 0. This patch enables the timers if DumpEntryPointStatsToCSV is set, because in most cases you dump these stats to get a detailed view on analyzer performance. -- CPP-7097 Full diff: https://github.com/llvm/llvm-project/pull/162089.diff 5 Files Affected:
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h
index 448e40269ca2d..d02f77194ad8c 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h
@@ -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;
@@ -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());
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
index 761395260a0cf..db4aec7c84754 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
@@ -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) {}
@@ -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;
diff --git a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
index 62ae62f2f2154..33c5df8132beb 100644
--- a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
+++ b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
@@ -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;
+ // 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;
@@ -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
@@ -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();
@@ -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;
}
@@ -159,6 +177,17 @@ 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 << "\",\"";
@@ -166,14 +195,15 @@ void Registry::Snapshot::dumpAsCSV(llvm::raw_ostream &OS) const {
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());
@@ -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) {
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index cf01e2f37c662..a5efa901b0714 100644
--- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -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>
@@ -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>(
@@ -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,
@@ -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);
}
}
@@ -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.
//===----------------------------------------------------------------------===//
@@ -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.
@@ -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);
}
diff --git a/clang/test/Analysis/analyzer-stats/entry-point-stats.cpp b/clang/test/Analysis/analyzer-stats/entry-point-stats.cpp
index 9cbe04550a8d3..b9099c6248108 100644
--- a/clang/test/Analysis/analyzer-stats/entry-point-stats.cpp
+++ b/clang/test/Analysis/analyzer-stats/entry-point-stats.cpp
@@ -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]+}}",
@@ -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]+}}",
@@ -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
|
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; |
There was a problem hiding this comment.
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?)
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Register and dump per-entry-point time taken by the path-sensitive and syntax-only analyses stages in milliseconds (SyntaxRunningTime, PathRunningTime).
Also introduce a difference between unset stats and 0 for statistics categories for which it makes sense. So now if the syntax analysis does not run for a certain entry point, it's SyntaxRunningTime will have an empty value and not 0.
This patch enables the timers if DumpEntryPointStatsToCSV is set, because in most cases you dump these stats to get a detailed view on analyzer performance.
--
CPP-7097