1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
//=== UnsignedStatDemo.cpp --------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This checker demonstrates the use of UnsignedEPStat for per-entry-point
// statistics. It conditionally sets a statistic based on the entry point name.
//
//===----------------------------------------------------------------------===//
#include "CheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/MemoryBuffer.h"
#include "gtest/gtest.h"
#include <optional>
using namespace clang;
using namespace ento;
static UnsignedEPStat DemoStat("DemoStat");
namespace {
class UnsignedStatTesterChecker : public Checker<check::BeginFunction> {
public:
void checkBeginFunction(CheckerContext &C) const {
StringRef Name;
if (const Decl *D = C.getLocationContext()->getDecl())
if (const FunctionDecl *F = D->getAsFunction())
Name = F->getName();
// Conditionally set the statistic based on the function name (leaving it
// undefined for all other functions)
if (Name == "func_one")
DemoStat.set(1);
else if (Name == "func_two")
DemoStat.set(2);
else
; // For any other function (e.g., "func_none") don't set the statistic
}
};
void addUnsignedStatTesterChecker(AnalysisASTConsumer &AnalysisConsumer,
AnalyzerOptions &AnOpts) {
AnOpts.CheckersAndPackages = {{"test.DemoStatChecker", true}};
AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
Registry.addChecker<UnsignedStatTesterChecker>(
"test.DemoStatChecker", "DescriptionOfDemoStatChecker");
});
}
// Find the index of a column in the CSV header.
// Returns std::nullopt if the column is not found.
static std::optional<unsigned>
findColumnIndex(llvm::ArrayRef<llvm::StringRef> Header,
llvm::StringRef ColumnName) {
auto Iter = llvm::find(Header, ColumnName);
if (Iter != Header.end())
return std::distance(Header.begin(), Iter);
return std::nullopt;
}
// Parse CSV content and extract a mapping from one column to another.
// KeyColumn is used as the map key (e.g., "DebugName").
// ValueColumn is used as the map value (e.g., "DemoStat").
// Returns a map from key column values to value column values.
static llvm::StringMap<std::string>
parseCSVColumnMapping(llvm::StringRef CSVContent, llvm::StringRef KeyColumn,
llvm::StringRef ValueColumn) {
llvm::StringMap<std::string> Result;
// Parse CSV: first line is header, subsequent lines are data
llvm::SmallVector<llvm::StringRef, 8> Lines;
CSVContent.split(Lines, '\n', -1, false);
if (Lines.size() < 2) // Need at least header + one data row
return Result;
// Parse header to find column indices
llvm::SmallVector<llvm::StringRef, 32> Header;
Lines[0].split(Header, ',');
std::optional<unsigned> KeyIdx = findColumnIndex(Header, KeyColumn);
std::optional<unsigned> ValueIdx = findColumnIndex(Header, ValueColumn);
if (!KeyIdx || !ValueIdx)
return Result;
// Parse data rows and extract mappings
for (auto Line : llvm::drop_begin(Lines)) {
llvm::SmallVector<llvm::StringRef, 32> Row;
Line.split(Row, ',');
if (Row.size() <= std::max(*KeyIdx, *ValueIdx))
continue;
llvm::StringRef KeyVal = Row[*KeyIdx].trim().trim('"');
llvm::StringRef ValueVal = Row[*ValueIdx].trim().trim('"');
if (!KeyVal.empty())
Result[KeyVal] = ValueVal.str();
}
return Result;
}
TEST(UnsignedStat, ExplicitlySetUnsignedStatistic) {
llvm::SmallString<128> TempMetricsCsvPath;
std::error_code EC =
llvm::sys::fs::createTemporaryFile("ep_stats", "csv", TempMetricsCsvPath);
ASSERT_FALSE(EC);
std::vector<std::string> Args = {
"-Xclang", "-analyzer-config", "-Xclang",
std::string("dump-entry-point-stats-to-csv=") +
TempMetricsCsvPath.str().str()};
// Clean up on exit
auto Cleanup = llvm::make_scope_exit(
[&]() { llvm::sys::fs::remove(TempMetricsCsvPath); });
EXPECT_TRUE(runCheckerOnCodeWithArgs<addUnsignedStatTesterChecker>(
R"cpp(
void func_one() {}
void func_two() {}
void func_none() {}
)cpp",
Args));
auto BufferOrError = llvm::MemoryBuffer::getFile(TempMetricsCsvPath);
ASSERT_TRUE(BufferOrError);
llvm::StringRef CSVContent = BufferOrError.get()->getBuffer();
// Parse the CSV and extract function statistics
llvm::StringMap<std::string> FunctionStats =
parseCSVColumnMapping(CSVContent, "DebugName", "DemoStat");
// Verify the expected values
ASSERT_TRUE(FunctionStats.count("func_one()"));
EXPECT_EQ(FunctionStats["func_one()"], "1");
ASSERT_TRUE(FunctionStats.count("func_two()"));
EXPECT_EQ(FunctionStats["func_two()"], "2");
ASSERT_TRUE(FunctionStats.count("func_none()"));
EXPECT_EQ(FunctionStats["func_none()"], ""); // Not set, should be empty
}
} // namespace
|