//===- EntryPointStats.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 // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointStats.h" #include "clang/AST/DeclBase.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/raw_ostream.h" #include using namespace clang; using namespace ento; namespace { struct Registry { std::vector BoolStats; std::vector CounterStats; std::vector UnsignedMaxStats; std::vector UnsignedStats; bool IsLocked = false; struct Snapshot { const Decl *EntryPoint; std::vector BoolStatValues; std::vector UnsignedStatValues; void dumpAsCSV(llvm::raw_ostream &OS) const; }; std::vector Snapshots; }; } // namespace static llvm::ManagedStatic StatsRegistry; namespace { template void enumerateStatVectors(const Callback &Fn) { Fn(StatsRegistry->BoolStats); Fn(StatsRegistry->CounterStats); Fn(StatsRegistry->UnsignedMaxStats); Fn(StatsRegistry->UnsignedStats); } } // namespace static void checkStatName(const EntryPointStat *M) { #ifdef NDEBUG return; #endif // NDEBUG constexpr std::array AllowedSpecialChars = { '+', '-', '_', '=', ':', '(', ')', '@', '!', '~', '$', '%', '^', '&', '*', '\'', ';', '<', '>', '/'}; for (unsigned char C : M->name()) { if (!std::isalnum(C) && !llvm::is_contained(AllowedSpecialChars, C)) { llvm::errs() << "Stat name \"" << M->name() << "\" contains character '" << C << "' (" << static_cast(C) << ") that is not allowed."; assert(false && "The Stat name contains unallowed character"); } } } void EntryPointStat::lockRegistry() { auto CmpByNames = [](const EntryPointStat *L, const EntryPointStat *R) { return L->name() < R->name(); }; enumerateStatVectors( [CmpByNames](auto &Stats) { llvm::sort(Stats, CmpByNames); }); enumerateStatVectors( [](const auto &Stats) { llvm::for_each(Stats, checkStatName); }); StatsRegistry->IsLocked = true; } [[maybe_unused]] static bool isRegistered(llvm::StringLiteral Name) { auto ByName = [Name](const EntryPointStat *M) { return M->name() == Name; }; bool Result = false; enumerateStatVectors([ByName, &Result](const auto &Stats) { Result = Result || llvm::any_of(Stats, ByName); }); return Result; } BoolEPStat::BoolEPStat(llvm::StringLiteral Name) : EntryPointStat(Name) { assert(!StatsRegistry->IsLocked); assert(!isRegistered(Name)); StatsRegistry->BoolStats.push_back(this); } CounterEPStat::CounterEPStat(llvm::StringLiteral Name) : EntryPointStat(Name) { assert(!StatsRegistry->IsLocked); assert(!isRegistered(Name)); StatsRegistry->CounterStats.push_back(this); } UnsignedMaxEPStat::UnsignedMaxEPStat(llvm::StringLiteral Name) : EntryPointStat(Name) { assert(!StatsRegistry->IsLocked); assert(!isRegistered(Name)); StatsRegistry->UnsignedMaxStats.push_back(this); } UnsignedEPStat::UnsignedEPStat(llvm::StringLiteral Name) : EntryPointStat(Name) { assert(!StatsRegistry->IsLocked); assert(!isRegistered(Name)); StatsRegistry->UnsignedStats.push_back(this); } static std::vector consumeUnsignedStats() { std::vector Result; Result.reserve(StatsRegistry->CounterStats.size() + StatsRegistry->UnsignedMaxStats.size() + StatsRegistry->UnsignedStats.size()); for (auto *M : StatsRegistry->CounterStats) { Result.push_back(M->value()); M->reset(); } for (auto *M : StatsRegistry->UnsignedMaxStats) { Result.push_back(M->value()); M->reset(); } for (auto *M : StatsRegistry->UnsignedStats) { Result.push_back(M->value()); M->reset(); } return Result; } static std::vector getStatNames() { std::vector Ret; auto GetName = [](const EntryPointStat *M) { return M->name(); }; enumerateStatVectors([GetName, &Ret](const auto &Stats) { transform(Stats, std::back_inserter(Ret), GetName); }); return Ret; } void Registry::Snapshot::dumpAsCSV(llvm::raw_ostream &OS) const { OS << '"'; llvm::printEscapedString( clang::AnalysisDeclContext::getFunctionName(EntryPoint), OS); OS << "\", "; auto PrintAsBool = [&OS](bool B) { OS << (B ? "true" : "false"); }; llvm::interleaveComma(BoolStatValues, OS, PrintAsBool); OS << ((BoolStatValues.empty() || UnsignedStatValues.empty()) ? "" : ", "); llvm::interleaveComma(UnsignedStatValues, OS); } static std::vector consumeBoolStats() { std::vector Result; Result.reserve(StatsRegistry->BoolStats.size()); for (auto *M : StatsRegistry->BoolStats) { Result.push_back(M->value()); M->reset(); } return Result; } void EntryPointStat::takeSnapshot(const Decl *EntryPoint) { auto BoolValues = consumeBoolStats(); auto UnsignedValues = consumeUnsignedStats(); StatsRegistry->Snapshots.push_back( {EntryPoint, std::move(BoolValues), std::move(UnsignedValues)}); } void EntryPointStat::dumpStatsAsCSV(llvm::StringRef FileName) { std::error_code EC; llvm::raw_fd_ostream File(FileName, EC, llvm::sys::fs::OF_Text); if (EC) return; dumpStatsAsCSV(File); } void EntryPointStat::dumpStatsAsCSV(llvm::raw_ostream &OS) { OS << "EntryPoint, "; llvm::interleaveComma(getStatNames(), OS); OS << "\n"; std::vector Rows; Rows.reserve(StatsRegistry->Snapshots.size()); for (const auto &Snapshot : StatsRegistry->Snapshots) { std::string Row; llvm::raw_string_ostream RowOs(Row); Snapshot.dumpAsCSV(RowOs); RowOs << "\n"; Rows.push_back(RowOs.str()); } llvm::sort(Rows); for (const auto &Row : Rows) { OS << Row; } }