Skip to content

Commit 07bcb96

Browse files
committed
[clang][analyzer] Record never-set statistics as empty CSV cells
1 parent e297184 commit 07bcb96

File tree

4 files changed

+66
-37
lines changed

4 files changed

+66
-37
lines changed

clang/docs/analyzer/developer-docs/Statistics.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ However, note that with ``LLVM_ENABLE_STATS`` disabled, only storage of the valu
2222
If you want to define a statistic only for entry point, EntryPointStats.h has four classes at your disposal:
2323

2424

25-
- ``UnsignedEPStat`` - an unsigned value assigned at most once per entry point. For example: "the number of source characters in an entry-point body".
25+
- ``UnsignedEPStat`` - an unsigned value assigned at most once per entry point. For example: "the number of source characters in an entry-point body". If no value is assigned during analysis of an entry point, the corresponding CSV cell will be empty.
2626
- ``CounterEPStat`` - an additive statistic. It starts with 0 and you can add to it as many times as needed. For example: "the number of bugs discovered".
2727
- ``UnsignedMaxEPStat`` - a maximizing statistic. It starts with 0 and when you join it with a value, it picks the maximum of the previous value and the new one. For example, "the longest execution path of a bug".
2828

clang/include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class UnsignedEPStat : public EntryPointStat {
8585

8686
public:
8787
explicit UnsignedEPStat(llvm::StringLiteral Name);
88-
unsigned value() const { return Value.value_or(0); }
88+
std::optional<unsigned> value() const { return Value; }
8989
void reset() { Value.reset(); }
9090
void set(unsigned V) {
9191
assert(!Value.has_value());

clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,21 @@ using namespace ento;
2424

2525
namespace {
2626
struct Registry {
27+
std::vector<UnsignedEPStat *> ExplicitlySetStats;
28+
std::vector<UnsignedMaxEPStat *> MaxStats;
2729
std::vector<CounterEPStat *> CounterStats;
28-
std::vector<UnsignedMaxEPStat *> UnsignedMaxStats;
29-
std::vector<UnsignedEPStat *> UnsignedStats;
3030

3131
bool IsLocked = false;
3232

3333
struct Snapshot {
3434
const Decl *EntryPoint;
35-
std::vector<unsigned> UnsignedStatValues;
35+
// Explicitly set statistics may not have a value set, so they are separate
36+
// from other unsigned statistics
37+
std::vector<std::optional<unsigned>> ExplicitlySetStatValues;
38+
// These are counting and maximizing statistics that initialize to 0, which
39+
// is meaningful even if they are never updated, so their value is always
40+
// present.
41+
std::vector<unsigned> MaxOrCountStatValues;
3642

3743
void dumpAsCSV(llvm::raw_ostream &OS) const;
3844
};
@@ -46,9 +52,12 @@ static llvm::ManagedStatic<Registry> StatsRegistry;
4652

4753
namespace {
4854
template <typename Callback> void enumerateStatVectors(const Callback &Fn) {
55+
// This order is important, it matches the order of the Snapshot fields:
56+
// - ExplicitlySetStatValues
57+
Fn(StatsRegistry->ExplicitlySetStats);
58+
// - MaxOrCountStatValues
59+
Fn(StatsRegistry->MaxStats);
4960
Fn(StatsRegistry->CounterStats);
50-
Fn(StatsRegistry->UnsignedMaxStats);
51-
Fn(StatsRegistry->UnsignedStats);
5261
}
5362
} // namespace
5463

@@ -101,30 +110,37 @@ UnsignedMaxEPStat::UnsignedMaxEPStat(llvm::StringLiteral Name)
101110
: EntryPointStat(Name) {
102111
assert(!StatsRegistry->IsLocked);
103112
assert(!isRegistered(Name));
104-
StatsRegistry->UnsignedMaxStats.push_back(this);
113+
StatsRegistry->MaxStats.push_back(this);
105114
}
106115

107116
UnsignedEPStat::UnsignedEPStat(llvm::StringLiteral Name)
108117
: EntryPointStat(Name) {
109118
assert(!StatsRegistry->IsLocked);
110119
assert(!isRegistered(Name));
111-
StatsRegistry->UnsignedStats.push_back(this);
120+
StatsRegistry->ExplicitlySetStats.push_back(this);
112121
}
113122

114-
static std::vector<unsigned> consumeUnsignedStats() {
115-
std::vector<unsigned> Result;
116-
Result.reserve(StatsRegistry->CounterStats.size() +
117-
StatsRegistry->UnsignedMaxStats.size() +
118-
StatsRegistry->UnsignedStats.size());
119-
for (auto *M : StatsRegistry->CounterStats) {
123+
static std::vector<std::optional<unsigned>>
124+
consumeExplicitlySetStats() {
125+
std::vector<std::optional<unsigned>> Result;
126+
Result.reserve(StatsRegistry->ExplicitlySetStats.size());
127+
for (auto *M : StatsRegistry->ExplicitlySetStats) {
120128
Result.push_back(M->value());
121129
M->reset();
122130
}
123-
for (auto *M : StatsRegistry->UnsignedMaxStats) {
131+
return Result;
132+
}
133+
134+
static std::vector<unsigned> consumeMaxAndCounterStats() {
135+
std::vector<unsigned> Result;
136+
Result.reserve(StatsRegistry->CounterStats.size() +
137+
StatsRegistry->MaxStats.size());
138+
// Order is important, it must match the order in enumerateStatVectors
139+
for (auto *M : StatsRegistry->MaxStats) {
124140
Result.push_back(M->value());
125141
M->reset();
126142
}
127-
for (auto *M : StatsRegistry->UnsignedStats) {
143+
for (auto *M : StatsRegistry->CounterStats) {
128144
Result.push_back(M->value());
129145
M->reset();
130146
}
@@ -150,20 +166,33 @@ static std::string getUSR(const Decl *D) {
150166
}
151167

152168
void Registry::Snapshot::dumpAsCSV(llvm::raw_ostream &OS) const {
169+
auto printAsUnsignOpt = [&OS](std::optional<unsigned> U) {
170+
OS << (U.has_value() ? std::to_string(*U) : "");
171+
};
172+
auto commaIfNeeded = [&OS](const auto &Vec1, const auto &Vec2) {
173+
if (!Vec1.empty() && !Vec2.empty())
174+
OS << ",";
175+
};
176+
auto printAsUnsigned = [&OS](unsigned U) { OS << U; };
177+
153178
OS << '"';
154179
llvm::printEscapedString(getUSR(EntryPoint), OS);
155180
OS << "\",\"";
156181
OS << StatsRegistry->EscapedCPPFileName << "\",\"";
157182
llvm::printEscapedString(
158183
clang::AnalysisDeclContext::getFunctionName(EntryPoint), OS);
159-
OS << "\"";
160-
OS << (UnsignedStatValues.empty() ? "" : ",");
161-
llvm::interleave(UnsignedStatValues, OS, [&OS](unsigned U) { OS << U; }, ",");
184+
OS << "\",";
185+
llvm::interleave(ExplicitlySetStatValues, OS, printAsUnsignOpt, ",");
186+
commaIfNeeded(ExplicitlySetStatValues, MaxOrCountStatValues);
187+
llvm::interleave(MaxOrCountStatValues, OS, printAsUnsigned, ",");
162188
}
163189

164190
void EntryPointStat::takeSnapshot(const Decl *EntryPoint) {
165-
auto UnsignedValues = consumeUnsignedStats();
166-
StatsRegistry->Snapshots.push_back({EntryPoint, std::move(UnsignedValues)});
191+
auto ExplicitlySetValues = consumeExplicitlySetStats();
192+
auto MaxOrCounterValues = consumeMaxAndCounterStats();
193+
StatsRegistry->Snapshots.push_back({EntryPoint,
194+
std::move(ExplicitlySetValues),
195+
std::move(MaxOrCounterValues)});
167196
}
168197

169198
void EntryPointStat::dumpStatsAsCSV(llvm::StringRef FileName) {

clang/test/Analysis/analyzer-stats/entry-point-stats.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@
66
//
77
// CHECK: {
88
// CHECK-NEXT: "c:@F@fib#i#": {
9-
// CHECK-NEXT: "File": "{{.*}}entry-point-stats.cpp",
9+
// CHECK-NEXT: "File": "{{.*}}/entry-point-stats.cpp",
1010
// CHECK-NEXT: "DebugName": "fib(unsigned int)",
11+
// CHECK-NEXT: "PathRunningTime": "",
12+
// CHECK-NEXT: "MaxBugClassSize": "{{[0-9]+}}",
13+
// CHECK-NEXT: "MaxCFGSize": "{{[0-9]+}}",
14+
// CHECK-NEXT: "MaxQueueSize": "{{[0-9]+}}",
15+
// CHECK-NEXT: "MaxReachableSize": "{{[0-9]+}}",
16+
// CHECK-NEXT: "MaxTimeSpentSolvingZ3Queries": "{{[0-9]+}}",
17+
// CHECK-NEXT: "MaxValidBugClassSize": "{{[0-9]+}}",
1118
// CHECK-NEXT: "NumBlocks": "{{[0-9]+}}",
1219
// CHECK-NEXT: "NumBlocksUnreachable": "{{[0-9]+}}",
1320
// CHECK-NEXT: "NumCTUSteps": "{{[0-9]+}}",
@@ -33,18 +40,18 @@
3340
// CHECK-NEXT: "NumTimesZ3SpendsTooMuchTimeOnASingleEQClass": "{{[0-9]+}}",
3441
// CHECK-NEXT: "NumTimesZ3TimedOut": "{{[0-9]+}}",
3542
// CHECK-NEXT: "NumZ3QueriesDone": "{{[0-9]+}}",
36-
// CHECK-NEXT: "TimeSpentSolvingZ3Queries": "{{[0-9]+}}",
43+
// CHECK-NEXT: "TimeSpentSolvingZ3Queries": "{{[0-9]+}}"
44+
// CHECK-NEXT: },
45+
// CHECK-NEXT: "c:@F@main#I#**C#": {
46+
// CHECK-NEXT: "File": "{{.*}}/entry-point-stats.cpp",
47+
// CHECK-NEXT: "DebugName": "main(int, char **)",
48+
// CHECK-NEXT: "PathRunningTime": "",
3749
// CHECK-NEXT: "MaxBugClassSize": "{{[0-9]+}}",
3850
// CHECK-NEXT: "MaxCFGSize": "{{[0-9]+}}",
3951
// CHECK-NEXT: "MaxQueueSize": "{{[0-9]+}}",
4052
// CHECK-NEXT: "MaxReachableSize": "{{[0-9]+}}",
4153
// CHECK-NEXT: "MaxTimeSpentSolvingZ3Queries": "{{[0-9]+}}",
4254
// CHECK-NEXT: "MaxValidBugClassSize": "{{[0-9]+}}",
43-
// CHECK-NEXT: "PathRunningTime": "{{[0-9]+}}"
44-
// CHECK-NEXT: },
45-
// CHECK-NEXT: "c:@F@main#I#**C#": {
46-
// CHECK-NEXT: "File": "{{.*}}entry-point-stats.cpp",
47-
// CHECK-NEXT: "DebugName": "main(int, char **)",
4855
// CHECK-NEXT: "NumBlocks": "{{[0-9]+}}",
4956
// CHECK-NEXT: "NumBlocksUnreachable": "{{[0-9]+}}",
5057
// CHECK-NEXT: "NumCTUSteps": "{{[0-9]+}}",
@@ -70,14 +77,7 @@
7077
// CHECK-NEXT: "NumTimesZ3SpendsTooMuchTimeOnASingleEQClass": "{{[0-9]+}}",
7178
// CHECK-NEXT: "NumTimesZ3TimedOut": "{{[0-9]+}}",
7279
// CHECK-NEXT: "NumZ3QueriesDone": "{{[0-9]+}}",
73-
// CHECK-NEXT: "TimeSpentSolvingZ3Queries": "{{[0-9]+}}",
74-
// CHECK-NEXT: "MaxBugClassSize": "{{[0-9]+}}",
75-
// CHECK-NEXT: "MaxCFGSize": "{{[0-9]+}}",
76-
// CHECK-NEXT: "MaxQueueSize": "{{[0-9]+}}",
77-
// CHECK-NEXT: "MaxReachableSize": "{{[0-9]+}}",
78-
// CHECK-NEXT: "MaxTimeSpentSolvingZ3Queries": "{{[0-9]+}}",
79-
// CHECK-NEXT: "MaxValidBugClassSize": "{{[0-9]+}}",
80-
// CHECK-NEXT: "PathRunningTime": "{{[0-9]+}}"
80+
// CHECK-NEXT: "TimeSpentSolvingZ3Queries": "{{[0-9]+}}"
8181
// CHECK-NEXT: }
8282
// CHECK-NEXT: }
8383
// CHECK-NOT: non_entry_point

0 commit comments

Comments
 (0)