From d0badbe4b0b25ac95ca54fab490b7ba8a3c5c88e Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sun, 15 Oct 2023 19:40:09 +0200 Subject: [PATCH 1/7] Add some statistics to the edge functions in IDE --- .../phasar/Controller/AnalysisController.h | 33 ++++++++- .../phasar/DataFlow/IfdsIde/EdgeFunction.h | 72 +++++++++++++++++++ .../IfdsIde/Solver/FlowEdgeFunctionCache.h | 29 ++++++++ .../DataFlow/IfdsIde/Solver/IDESolver.h | 25 +++++++ include/phasar/Utils/Timer.h | 39 ++++++++++ include/phasar/Utils/TypeTraits.h | 4 ++ .../Problems/IDELinearConstantAnalysis.cpp | 25 ++++++- 7 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 include/phasar/Utils/Timer.h diff --git a/include/phasar/Controller/AnalysisController.h b/include/phasar/Controller/AnalysisController.h index 5ded47b64e..f598c55153 100644 --- a/include/phasar/Controller/AnalysisController.h +++ b/include/phasar/Controller/AnalysisController.h @@ -26,14 +26,20 @@ #include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" #include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" +#include "phasar/Utils/ChronoUtils.h" #include "phasar/Utils/EnumFlags.h" #include "phasar/Utils/IO.h" #include "phasar/Utils/Soundness.h" +#include "phasar/Utils/Timer.h" +#include #include #include #include +#include +#include + namespace psr { class AnalysisController { @@ -109,7 +115,17 @@ class AnalysisController { auto Problem = createAnalysisProblem(HA, std::forward(Args)...); SolverTy Solver(Problem, &HA.getICFG()); - Solver.solve(); + { + std::optional MeasureTime; + if (EmitterOptions & + AnalysisControllerEmitterOptions::EmitStatisticsAsText) { + MeasureTime.emplace([](hms Elapsed) { + llvm::outs() << "Elapsed: " << Elapsed << '\n'; + }); + } + + Solver.solve(); + } emitRequestedDataFlowResults(Solver); } @@ -127,6 +143,16 @@ class AnalysisController { LLVMTaintConfig makeTaintConfig(); + template + void statsEmitter(llvm::raw_ostream &OS, const T &Solver) { + OS << "The solver " << llvm::getTypeName() + << " does not support statistics output\n"; + } + template + void statsEmitter(llvm::raw_ostream &OS, const IDESolver &Solver) { + Solver.printEdgeFunctionStatistics(); + } + template void emitRequestedDataFlowResults(T &Solver) { if (EmitterOptions & AnalysisControllerEmitterOptions::EmitTextReport) { if (!ResultDirectory.empty()) { @@ -163,6 +189,11 @@ class AnalysisController { llvm::outs() << "Front-end support for 'EmitESGAsDot' to be implemented\n"; } + if (EmitterOptions & + AnalysisControllerEmitterOptions::EmitStatisticsAsText) { + + statsEmitter(llvm::outs(), Solver); + } } public: diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index 72f33565ac..1d38883961 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -21,11 +21,15 @@ #include "llvm/Support/raw_ostream.h" #include +#include #include #include #include #include +#include +#include + namespace psr { template class EdgeFunction; @@ -404,6 +408,12 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { FirstEF.EF, SecondEF, FirstEF.VTAndHeapAlloc.getInt()); } + [[nodiscard]] bool + referenceEquals(const EdgeFunction &Other) const noexcept { + return VTAndHeapAlloc.getPointer() == Other.VTAndHeapAlloc.getPointer() && + EF == Other.EF; + } + /// Checks for equality of two edge functions. Equality requires exact /// type-equality and value-equality based on operator== of the concrete edge /// functions that are compared. @@ -630,6 +640,31 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { return static_cast *>(EF)->Cache; } + [[nodiscard]] size_t getHashCode() const noexcept { + if (!VTAndHeapAlloc.getOpaqueValue()) { + return 0; + } + + return VTAndHeapAlloc.getPointer()->getHashCode( + EF, VTAndHeapAlloc.getPointer()); + } + + friend size_t hash_value(const EdgeFunction &EF) noexcept { // NOLINT + return EF.getHashCode(); + } + + static EdgeFunction getEmptyKey() noexcept { + return EdgeFunction(nullptr, + {llvm::DenseMapInfo::getEmptyKey(), + AllocationPolicy::SmallObjectOptimized}); + } + + static EdgeFunction getTombstoneKey() noexcept { + return EdgeFunction(nullptr, + {llvm::DenseMapInfo::getTombstoneKey(), + AllocationPolicy::SmallObjectOptimized}); + } + private: struct VTable { // NOLINTBEGIN(readability-identifier-naming) @@ -641,6 +676,7 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { void (*print)(const void *, llvm::raw_ostream &); bool (*isConstant)(const void *) noexcept; void (*destroy)(const void *, AllocationPolicy) noexcept; + size_t (*getHashCode)(const void *, const void *) noexcept; // NOLINTEND(readability-identifier-naming) }; @@ -704,6 +740,16 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { } } }, + [](const void *EF, const void *VT) noexcept -> size_t { + if constexpr (is_std_hashable_v) { + return std::hash{}(*getPtr(EF)); + } else if constexpr (is_llvm_hashable_v) { + using llvm::hash_value; + return hash_value(*getPtr(EF)); + } else { + return llvm::hash_combine(EF, VT); + } + }, }; // Utility ctor for (copy) construction. Increments the ref-count if @@ -729,6 +775,32 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { namespace llvm { +template struct DenseMapInfo> { + static inline auto getEmptyKey() noexcept { + return psr::EdgeFunction::getEmptyKey(); + } + static inline auto getTombstoneKey() noexcept { + return psr::EdgeFunction::getTombstoneKey(); + } + static inline auto getHashValue(const psr::EdgeFunction &EF) noexcept { + return EF.getHashCode(); + } + static inline auto isEqual(const psr::EdgeFunction &EF1, + const psr::EdgeFunction &EF2) noexcept { + if (EF1.referenceEquals(EF2)) { + return true; + } + auto Empty = getEmptyKey(); + auto Tombstone = getTombstoneKey(); + if (EF1.referenceEquals(Empty) || EF2.referenceEquals(Empty) || + EF1.referenceEquals(Tombstone) || EF2.referenceEquals(Tombstone)) { + return false; + } + + return EF1 == EF2; + } +}; + // LLVM is currently overhauling its casting system. Use the new variant once // possible! // Note: The new variant (With CastInfo) is not tested yet! diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index b9d89a88ea..4333b9521c 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -32,6 +32,9 @@ class Value; } // namespace llvm namespace psr { + +enum class EdgeFunctionKind { Normal, Call, Return, CallToReturn, Summary }; + template class DefaultMapKeyCompressor { public: using KeyType = KeyT; @@ -603,6 +606,32 @@ class FlowEdgeFunctionCache { } } + template void foreachCachedEdgeFunction(Handler Fn) const { + for (const auto &[Key, NormalFns] : NormalFunctionCache) { + for (const auto &[Set, EF] : NormalFns.EdgeFunctionMap) { + Fn(EF, EdgeFunctionKind::Normal); + } + } + + for (const auto &[Key, EF] : CallEdgeFunctionCache) { + Fn(EF, EdgeFunctionKind::Call); + } + + for (const auto &[Key, EF] : ReturnEdgeFunctionCache) { + Fn(EF, EdgeFunctionKind::Return); + } + + for (const auto &[Key, CTRFns] : CallToRetEdgeFunctionCache) { + for (const auto &[Set, EF] : CTRFns) { + Fn(EF, EdgeFunctionKind::CallToReturn); + } + } + + for (const auto &[Key, EF] : SummaryEdgeFunctionCache) { + Fn(EF, EdgeFunctionKind::Summary); + } + } + private: inline EdgeFuncInstKey createEdgeFunctionInstKey(n_t Lhs, n_t Rhs) { uint64_t Val = 0; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index ac730de2a5..363e4c2659 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -53,6 +53,8 @@ #include #include +#include + namespace psr { /// Solves the given IDETabulationProblem as described in the 1996 paper by @@ -255,6 +257,29 @@ class IDESolver std::move(ZeroValue)); } + void printEdgeFunctionStatistics(llvm::raw_ostream &OS = llvm::outs()) const { + std::array>, 5> UniqueEFs{}; + std::array TotalEFCount{}; + static constexpr std::array EFKind{ + "Normal", "Call", "Return", "CallToReturn", "Summary"}; + // TODO: Cache EFs + CachedFlowEdgeFunctions.foreachCachedEdgeFunction( + [&UniqueEFs, &TotalEFCount](EdgeFunction EF, + EdgeFunctionKind Kind) { + UniqueEFs[int(Kind)].insert(std::move(EF)); + TotalEFCount[int(Kind)]++; + }); + + size_t Ctr = 0; + for (const auto &[UEF, Count] : llvm::zip(UniqueEFs, TotalEFCount)) { + OS << "Kind: " << EFKind[Ctr] << ":\n"; + Ctr++; + + OS << " Total # EdgeFunctions: " << Count << '\n'; + OS << " Unique EdgeFunctions: " << UEF.size() << '\n'; + } + } + protected: /// Lines 13-20 of the algorithm; processing a call site in the caller's /// context. diff --git a/include/phasar/Utils/Timer.h b/include/phasar/Utils/Timer.h new file mode 100644 index 0000000000..8cf62fe714 --- /dev/null +++ b/include/phasar/Utils/Timer.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) 2023 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_UTILS_TIMER_H +#define PHASAR_UTILS_TIMER_H + +#include "phasar/Utils/ChronoUtils.h" + +#include "llvm/ADT/FunctionExtras.h" + +#include + +namespace psr { +class Timer { +public: + Timer(llvm::unique_function WithElapsed) noexcept + : WithElapsed(std::move(WithElapsed)), + Start(std::chrono::steady_clock::now()) {} + + ~Timer() { + if (WithElapsed) { + auto End = std::chrono::steady_clock::now(); + WithElapsed(hms{End - Start}); + } + } + +private: + llvm::unique_function WithElapsed; + std::chrono::steady_clock::time_point Start; +}; +} // namespace psr + +#endif // PHASAR_UTILS_TIMER_H diff --git a/include/phasar/Utils/TypeTraits.h b/include/phasar/Utils/TypeTraits.h index 77bcfa36ae..f70f5edd92 100644 --- a/include/phasar/Utils/TypeTraits.h +++ b/include/phasar/Utils/TypeTraits.h @@ -105,6 +105,10 @@ struct is_llvm_hashable : std::false_type {}; // NOLINT template struct is_llvm_hashable()))> // NOLINT : std::true_type {}; +template +struct is_llvm_hashable()))> // NOLINT + : std::true_type {}; template