From f6e394f0e2e4ca82941c6cd62a324d5db03635f5 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sun, 14 Jun 2026 20:13:45 +0200 Subject: [PATCH 01/18] Attempt to reduce overhead of PAMM counter increments --- .../DataFlow/IfdsIde/Solver/IDESolver.h | 2 +- include/phasar/Utils/PAMM.h | 39 +++++++++++++++---- include/phasar/Utils/PAMMMacros.h | 9 +++-- include/phasar/Utils/Utilities.h | 12 ------ lib/Utils/PAMM.cpp | 37 +++++++++++------- unittests/Utils/PAMMTest.cpp | 30 ++++++++++++++ 6 files changed, 93 insertions(+), 36 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 8a4c512b9c..a948baf0d7 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -1409,7 +1409,7 @@ class IDESolver if (ICF->isCallSite(Edge.first)) { ValidInCallerContext[Edge.second].insert(D2s.begin(), D2s.end()); } - IF_LOG_LEVEL_ENABLED(DEBUG, [this](const auto &D2s) { + IF_LOG_LEVEL_ENABLED(DEBUG, [](const auto &D2s) { for (auto D2 : D2s) { PHASAR_LOG_LEVEL(DEBUG, "d2: " << DToString(D2)); } diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 00f4bb3c78..99abb1a897 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -56,8 +56,8 @@ namespace psr { /// other macro from this class. class PAMM final { public: - using TimePoint_t = std::chrono::high_resolution_clock::time_point; - using Duration_t = std::chrono::milliseconds; + using TimePoint_t = std::chrono::steady_clock::time_point; + using Duration_t = std::chrono::nanoseconds; PAMM() noexcept = default; ~PAMM() = default; @@ -127,25 +127,32 @@ class PAMM final { /// \brief Registers a new counter under the given counter id - associated /// macro: REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL). /// \param CounterId Unique counter id. - void regCounter(llvm::StringRef CounterId, unsigned IntialValue = 0); + void regCounter(llvm::StringRef CounterId, uint64_t IntialValue = 0); /// \brief Increases the count for the given counter - associated macro: /// INC_COUNTER(COUNTER_ID, VALUE, SEV_LVL). /// \param CounterId Unique counter id. /// \param CValue to be added to the current counter. - void incCounter(llvm::StringRef CounterId, unsigned CValue = 1); + void incCounter(llvm::StringRef CounterId, uint64_t CValue = 1); /// \brief Decreases the count for the given counter - associated macro: /// DEC_COUNTER(COUNTER_ID, VALUE, SEV_LVL). /// \param CounterId Unique counter id. /// \param CValue to be subtracted from the current counter. - void decCounter(llvm::StringRef CounterId, unsigned CValue = 1); + void decCounter(llvm::StringRef CounterId, uint64_t CValue = 1); /// The associated macro does not check PAMM's severity level explicitly. /// \brief Returns the current count for the given counter - associated macro: /// GET_COUNTER(COUNTER_ID). /// \param CounterId Unique counter id. - std::optional getCounter(llvm::StringRef CounterId); + std::optional getCounter(llvm::StringRef CounterId); + + /// \brief Returns a reference to the storage of the given counter, + /// registering it with value 0 if it does not exist yet. The + /// returned reference remains valid for the lifetime of this PAMM + /// instance, even if other counters are registered afterwards. + /// \param CounterId Unique counter id. + [[nodiscard]] uint64_t &getOrCreateCounterRef(llvm::StringRef CounterId); /// The associated macro does not check PAMM's severity level explicitly. /// \brief Sums the counts for the given counter ids - associated macro: @@ -172,6 +179,24 @@ class PAMM final { void addToHistogram(llvm::StringRef HistogramId, llvm::StringRef DataPointId, uint64_t DataPointValue = 1); + /// \brief Adds a new observed data point directly to the given + /// histogram's bucket map, skipping the histogram-id lookup. + /// \param Hist Reference to a histogram's bucket map, as returned by + /// getOrCreateHistogramRef(). + /// \param DataPointId ID of the given data point. + /// \param DataPointValue Value of the given data point. + void addToHistogram(llvm::StringMap &Hist, + llvm::StringRef DataPointId, uint64_t DataPointValue = 1); + + /// \brief Returns a reference to the bucket map of the given + /// histogram, registering an empty histogram if it does not exist + /// yet. The returned reference remains valid for the lifetime of + /// this PAMM instance, even if other histograms are registered + /// afterwards. + /// \param HistogramId Unique histogram id. + [[nodiscard]] llvm::StringMap & + getOrCreateHistogramRef(llvm::StringRef HistogramId); + void stopAllTimers(); void printTimers(llvm::raw_ostream &OS); @@ -200,7 +225,7 @@ class PAMM final { llvm::StringMap> StoppedTimer; llvm::StringMap>> RepeatingTimer; - llvm::StringMap Counter; + llvm::StringMap Counter; llvm::StringMap> Histogram; }; diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index cc74e66319..f7029f5c39 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -71,11 +71,13 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = } #define INC_COUNTER(COUNTER_ID, VALUE, SEV_LVL) \ if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ - pamm.incCounter(COUNTER_ID, VALUE); \ + static uint64_t &PammCounterRef = pamm.getOrCreateCounterRef(COUNTER_ID); \ + PammCounterRef += (VALUE); \ } #define DEC_COUNTER(COUNTER_ID, VALUE, SEV_LVL) \ if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ - pamm.decCounter(COUNTER_ID, VALUE); \ + static uint64_t &PammCounterRef = pamm.getOrCreateCounterRef(COUNTER_ID); \ + PammCounterRef -= (VALUE); \ } #define GET_COUNTER(COUNTER_ID) pamm.getCounter(COUNTER_ID) #define GET_SUM_COUNT(...) pamm.getSumCount(__VA_ARGS__) @@ -86,7 +88,8 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = } #define ADD_TO_HISTOGRAM(HISTOGRAM_ID, DATAPOINT_ID, DATAPOINT_VALUE, SEV_LVL) \ if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ - pamm.addToHistogram(HISTOGRAM_ID, adl_to_string(DATAPOINT_ID), \ + static auto &PammHistRef = pamm.getOrCreateHistogramRef(HISTOGRAM_ID); \ + pamm.addToHistogram(PammHistRef, adl_to_string(DATAPOINT_ID), \ DATAPOINT_VALUE); \ } diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index 0b534246eb..4204a0af67 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -305,18 +305,6 @@ struct SecondFn { } }; -template -llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const std::optional &Opt) { - if (Opt) { - OS << *Opt; - } else { - OS << ""; - } - - return OS; -} - template requires(!std::is_pointer_v) LLVM_ATTRIBUTE_ALWAYS_INLINE T &assertNotNull(T &Value) { diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index fe7072d59b..3cff9ae76e 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -57,7 +57,7 @@ void PAMM::startTimer(llvm::StringRef TimerId) { llvm::report_fatal_error("Do not start an already running timer"); } - PAMM::TimePoint_t Start = std::chrono::high_resolution_clock::now(); + PAMM::TimePoint_t Start = std::chrono::steady_clock::now(); It->second = Start; } @@ -81,7 +81,7 @@ void PAMM::stopTimer(llvm::StringRef TimerId, bool PauseTimer) { assert(TimerRunning && "stopTimer failed because timer was already stopped"); if (LLVM_LIKELY(ValidTimerId)) { - PAMM::TimePoint_t End = std::chrono::high_resolution_clock::now(); + PAMM::TimePoint_t End = std::chrono::steady_clock::now(); PAMM::TimePoint_t Start = RunningIt->second; RunningTimer.erase(RunningIt); auto P = make_pair(Start, End); @@ -97,7 +97,7 @@ uint64_t PAMM::elapsedTime(llvm::StringRef TimerId) { auto RunningIt = RunningTimer.find(TimerId); if (RunningIt != RunningTimer.end()) { - PAMM::TimePoint_t End = std::chrono::high_resolution_clock::now(); + PAMM::TimePoint_t End = std::chrono::steady_clock::now(); PAMM::TimePoint_t Start = RunningIt->second; auto Duration = std::chrono::duration_cast(End - Start); return Duration.count(); @@ -145,16 +145,16 @@ llvm::StringMap> PAMM::elapsedTimeOfRepeatingTimer() { } std::string PAMM::getPrintableDuration(uint64_t Duration) { - return hms(std::chrono::milliseconds{Duration}).str(); + return hms(Duration_t{Duration}).str(); } -void PAMM::regCounter(llvm::StringRef CounterId, unsigned IntialValue) { +void PAMM::regCounter(llvm::StringRef CounterId, uint64_t IntialValue) { [[maybe_unused]] auto [It, Inserted] = Counter.try_emplace(CounterId, IntialValue); assert(Inserted && "regCounter failed due to an invalid counter id"); } -void PAMM::incCounter(llvm::StringRef CounterId, unsigned CValue) { +void PAMM::incCounter(llvm::StringRef CounterId, uint64_t CValue) { auto It = Counter.find(CounterId); bool ValidCounterId = It != Counter.end(); assert(ValidCounterId && "incCounter failed due to an invalid counter id"); @@ -163,7 +163,7 @@ void PAMM::incCounter(llvm::StringRef CounterId, unsigned CValue) { } } -void PAMM::decCounter(llvm::StringRef CounterId, unsigned CValue) { +void PAMM::decCounter(llvm::StringRef CounterId, uint64_t CValue) { auto It = Counter.find(CounterId); bool ValidCounterId = It != Counter.end(); assert(ValidCounterId && "decCounter failed due to an invalid counter id"); @@ -172,7 +172,7 @@ void PAMM::decCounter(llvm::StringRef CounterId, unsigned CValue) { } } -std::optional PAMM::getCounter(llvm::StringRef CounterId) { +std::optional PAMM::getCounter(llvm::StringRef CounterId) { auto It = Counter.find(CounterId); bool ValidCounterId = It != Counter.end(); assert(ValidCounterId && "getCounter failed due to an invalid counter id"); @@ -182,6 +182,10 @@ std::optional PAMM::getCounter(llvm::StringRef CounterId) { return std::nullopt; } +uint64_t &PAMM::getOrCreateCounterRef(llvm::StringRef CounterId) { + return Counter[CounterId]; +} + template static std::optional getSumCountInternal(PAMM &P, ForwardIterator It, @@ -228,11 +232,18 @@ void PAMM::addToHistogram(llvm::StringRef HistogramId, return; } - auto [DataIt, Inserted] = - HistIt->second.try_emplace(DataPointId, DataPointValue); - if (!Inserted) { - DataIt->second += DataPointValue; - } + addToHistogram(HistIt->second, DataPointId, DataPointValue); +} + +void PAMM::addToHistogram(llvm::StringMap &Hist, + llvm::StringRef DataPointId, + uint64_t DataPointValue) { + Hist[DataPointId] += DataPointValue; +} + +llvm::StringMap & +PAMM::getOrCreateHistogramRef(llvm::StringRef HistogramId) { + return Histogram[HistogramId]; } void PAMM::stopAllTimers() { diff --git a/unittests/Utils/PAMMTest.cpp b/unittests/Utils/PAMMTest.cpp index 71aa2982e5..31e4adbdab 100644 --- a/unittests/Utils/PAMMTest.cpp +++ b/unittests/Utils/PAMMTest.cpp @@ -135,6 +135,36 @@ TEST_F(PAMMTest, HandleJSONOutput) { EXPECT_EQ(Hist, Gt); } +TEST_F(PAMMTest, HandleCounterRef) { + PAMM &Pamm = PAMM::getInstance(); + uint64_t &Ref1 = Pamm.getOrCreateCounterRef("cached"); + Ref1 += 5; + uint64_t &Ref2 = Pamm.getOrCreateCounterRef("cached"); + EXPECT_EQ(&Ref1, &Ref2) << "getOrCreateCounterRef must return a stable " + "reference to the same storage"; + Ref2 += 7; + EXPECT_EQ(Pamm.getCounter("cached"), 12U); +} + +TEST_F(PAMMTest, HandleHistogramRef) { + PAMM &Pamm = PAMM::getInstance(); + llvm::StringMap &Hist1 = Pamm.getOrCreateHistogramRef("hist"); + Pamm.addToHistogram(Hist1, "a", 3); + Pamm.addToHistogram(Hist1, "b", 1); + Pamm.addToHistogram(Hist1, "a", 2); + + llvm::StringMap &Hist2 = Pamm.getOrCreateHistogramRef("hist"); + EXPECT_EQ(&Hist1, &Hist2) << "getOrCreateHistogramRef must return a stable " + "reference to the same storage"; + + llvm::StringMap Gt = {{"a", 5}, {"b", 1}}; + EXPECT_EQ(Hist1, Gt); + + // the StringRef-based overload must still operate on the same histogram + Pamm.addToHistogram("hist", "a"); + EXPECT_EQ(Hist1.lookup("a"), 6U); +} + // main function for the test case int main(int Argc, char **Argv) { ::testing::InitGoogleTest(&Argc, Argv); From 6cca0426932897063bcd846544fed0fe38fd3a46 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Tue, 16 Jun 2026 20:44:53 +0200 Subject: [PATCH 02/18] Rework PAMM counters to be more efficient in hot loops & less tedious to work with (getting the string names right, ...) --- CMakeLists.txt | 7 +- .../IfdsIde/Solver/FlowEdgeFunctionCache.h | 206 +++++++++--------- .../DataFlow/IfdsIde/Solver/IDESolver.h | 143 ++++++------ include/phasar/Utils/PAMM.h | 148 ++++++++++++- include/phasar/Utils/PAMMMacros.h | 28 +-- include/phasar/Utils/TemplateString.h | 21 ++ .../ControlFlow/LLVMBasedCallGraphBuilder.cpp | 17 +- .../IfdsIde/Problems/IFDSConstAnalysis.cpp | 12 +- .../Passes/GeneralStatisticsAnalysis.cpp | 16 +- lib/Utils/PAMM.cpp | 23 +- 10 files changed, 389 insertions(+), 232 deletions(-) create mode 100644 include/phasar/Utils/TemplateString.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 96da3a1a42..4177899f46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,10 +197,9 @@ if (PHASAR_ENABLE_PIC) endif (PHASAR_ENABLE_PIC) # PAMM -if (NOT PHASAR_ENABLE_PAMM) - set(PHASAR_ENABLE_PAMM "Off" CACHE STRING "Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is 'Off')" FORCE) - set_property(CACHE PHASAR_ENABLE_PAMM PROPERTY STRINGS "Off" "Core" "Full") -endif() +set(PHASAR_ENABLE_PAMM "Off" CACHE STRING "Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is 'Off')") +set_property(CACHE PHASAR_ENABLE_PAMM PROPERTY STRINGS "Off" "Core" "Full") + if(PHASAR_ENABLE_PAMM STREQUAL "Core" AND NOT PHASAR_BUILD_UNITTESTS) set(PAMM_CORE ON) message(STATUS "PAMM metric severity level: Core") diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index 7521fb865c..ac5ab229da 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -90,6 +90,41 @@ class FlowEdgeFunctionCache { using FlowFunctionType = FlowFunction; using EdgeFunctionType = EdgeFunction; + PAMM_CATEGORY(FlowEdgeFunctionCache); + + // NOLINTBEGIN + REG_COUNTER(NormalFF_Construction, 0, Full); + REG_COUNTER(NormalFF_CacheHit, 0, Full); + // Counters for the call flow functions + REG_COUNTER(CallFF_Construction, 0, Full); + REG_COUNTER(CallFF_CacheHit, 0, Full); + // Counters for return flow functions + REG_COUNTER(ReturnFF_Construction, 0, Full); + REG_COUNTER(ReturnFF_CacheHit, 0, Full); + // Counters for the call to return flow functions + REG_COUNTER(CallToRetFF_Construction, 0, Full); + REG_COUNTER(CallToRetFF_CacheHit, 0, Full); + // Counters for the summary flow functions + REG_COUNTER(SummaryFF_Construction, 0, Full); + REG_COUNTER(SummaryFF_CacheHit, 0, Full); + // Counters for the normal edge functions + REG_COUNTER(NormalEF_Construction, 0, Full); + REG_COUNTER(NormalEF_CacheHit, 0, Full); + // Counters for the call edge functions + REG_COUNTER(CallEF_Construction, 0, Full); + REG_COUNTER(CallEF_CacheHit, 0, Full); + // Counters for the return edge functions + REG_COUNTER(ReturnEF_Construction, 0, Full); + REG_COUNTER(ReturnEF_CacheHit, 0, Full); + // Counters for the call to return edge functions + REG_COUNTER(CallToRetEF_Construction, 0, Full); + REG_COUNTER(CallToRetEF_CacheHit, 0, Full); + // Counters for the summary edge functions + REG_COUNTER(SummaryEF_Construction, 0, Full); + REG_COUNTER(SummaryEF_CacheHit, 0, Full); + + // NOLINTEND + public: // Ctor allows access to the IDEProblem in order to get access to flow and // edge function factory functions. @@ -97,38 +132,7 @@ class FlowEdgeFunctionCache { IDETabulationProblem &Problem) : Problem(Problem), AutoAddZero(Problem.getIFDSIDESolverConfig().autoAddZero()), - ZV(Problem.getZeroValue()) { - PAMM_GET_INSTANCE; - REG_COUNTER("Normal-FF Construction", 0, Full); - REG_COUNTER("Normal-FF Cache Hit", 0, Full); - // Counters for the call flow functions - REG_COUNTER("Call-FF Construction", 0, Full); - REG_COUNTER("Call-FF Cache Hit", 0, Full); - // Counters for return flow functions - REG_COUNTER("Return-FF Construction", 0, Full); - REG_COUNTER("Return-FF Cache Hit", 0, Full); - // Counters for the call to return flow functions - REG_COUNTER("CallToRet-FF Construction", 0, Full); - REG_COUNTER("CallToRet-FF Cache Hit", 0, Full); - // Counters for the summary flow functions - REG_COUNTER("Summary-FF Construction", 0, Full); - REG_COUNTER("Summary-FF Cache Hit", 0, Full); - // Counters for the normal edge functions - REG_COUNTER("Normal-EF Construction", 0, Full); - REG_COUNTER("Normal-EF Cache Hit", 0, Full); - // Counters for the call edge functions - REG_COUNTER("Call-EF Construction", 0, Full); - REG_COUNTER("Call-EF Cache Hit", 0, Full); - // Counters for the return edge functions - REG_COUNTER("Return-EF Construction", 0, Full); - REG_COUNTER("Return-EF Cache Hit", 0, Full); - // Counters for the call to return edge functions - REG_COUNTER("CallToRet-EF Construction", 0, Full); - REG_COUNTER("CallToRet-EF Cache Hit", 0, Full); - // Counters for the summary edge functions - REG_COUNTER("Summary-EF Construction", 0, Full); - REG_COUNTER("Summary-EF Cache Hit", 0, Full); - } + ZV(Problem.getZeroValue()) {} ~FlowEdgeFunctionCache() = default; @@ -143,7 +147,6 @@ class FlowEdgeFunctionCache { n_t Succ) { assertNotNull(Curr); assertNotNull(Succ); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Normal flow function factory call"); PHASAR_LOG_LEVEL(DEBUG, "(N) Curr Inst : " << NToString(Curr)); @@ -155,7 +158,7 @@ class FlowEdgeFunctionCache { // key, so getNormalEdgeFunction shares this entry via the same lookup. auto &NormalFE = NormalFunctionCache[std::move(Key)]; if (!NormalFE.FlowFuncPtr) { - INC_COUNTER("Normal-FF Construction", 1, Full); + NormalFF_Construction++; auto FF = Problem.getNormalFlowFunction(Curr, Succ); NormalFE.FlowFuncPtr = AutoAddZero ? std::make_unique(std::move(FF), ZV) @@ -163,7 +166,7 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); } else { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); - INC_COUNTER("Normal-FF Cache Hit", 1, Full); + NormalFF_CacheHit++; } return getPointerFrom(NormalFE.FlowFuncPtr); @@ -173,7 +176,6 @@ class FlowEdgeFunctionCache { f_t DestFun) { assertNotNull(CallSite); assertNotNull(DestFun); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Call flow function factory call"); PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " << NToString(CallSite)); @@ -182,14 +184,14 @@ class FlowEdgeFunctionCache { auto [It, Inserted] = CallFlowFunctionCache.try_emplace(std::move(Key)); if (Inserted) { - INC_COUNTER("Call-FF Construction", 1, Full); + CallFF_Construction++; auto FF = Problem.getCallFlowFunction(CallSite, DestFun); It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) : std::move(FF); PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); } else { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); - INC_COUNTER("Call-FF Cache Hit", 1, Full); + CallFF_CacheHit++; } return getPointerFrom(It->second); @@ -201,7 +203,6 @@ class FlowEdgeFunctionCache { assertNotNull(CalleeFun); assertNotNull(ExitInst); assertNotNull(RetSite); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Return flow function factory call"); PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); @@ -212,7 +213,7 @@ class FlowEdgeFunctionCache { auto [It, Inserted] = ReturnFlowFunctionCache.try_emplace(std::move(Key)); if (Inserted) { - INC_COUNTER("Return-FF Construction", 1, Full); + ReturnFF_Construction++; auto FF = Problem.getRetFlowFunction(CallSite, CalleeFun, ExitInst, RetSite); It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) @@ -221,7 +222,7 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); } else { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); - INC_COUNTER("Return-FF Cache Hit", 1, Full); + ReturnFF_CacheHit++; } return getPointerFrom(It->second); } @@ -232,7 +233,6 @@ class FlowEdgeFunctionCache { assertNotNull(CallSite); assertNotNull(RetSite); assertAllNotNull(Callees); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Call-to-Return flow function factory call"); @@ -247,7 +247,7 @@ class FlowEdgeFunctionCache { auto [It, Inserted] = CallToRetFlowFunctionCache.try_emplace(std::move(Key)); if (Inserted) { - INC_COUNTER("CallToRet-FF Construction", 1, Full); + CallToRetFF_Construction++; auto FF = Problem.getCallToRetFlowFunction(CallSite, RetSite, Callees); It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) : std::move(FF); @@ -255,7 +255,8 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); } else { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); - INC_COUNTER("CallToRet-FF Cache Hit", 1, Full); + CallToRetFF_CacheHit++; + CallToRetFF_CacheHit++; } return getPointerFrom(It->second); @@ -269,8 +270,8 @@ class FlowEdgeFunctionCache { f_t DestFun) { assertNotNull(CallSite); assertNotNull(DestFun); - // PAMM_GET_INSTANCE; - // INC_COUNTER("Summary-FF Construction", 1, Full); + + SummaryFF_Construction++; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Summary flow function factory call"); PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " << NToString(CallSite)); @@ -285,7 +286,6 @@ class FlowEdgeFunctionCache { assertNotNull(Curr); assertNotNull(Succ); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Normal edge function factory call"); PHASAR_LOG_LEVEL(DEBUG, "(N) Curr Inst : " << NToString(Curr)); @@ -298,14 +298,14 @@ class FlowEdgeFunctionCache { auto Ret = NormalFE.EdgeFunctionMap.getOrInsertLazy( createEdgeFunctionNodeKey(CurrNode, SuccNode), [&] { - INC_COUNTER("Normal-EF Construction", 1, Full); + NormalEF_Construction++; auto EF = Problem.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); return EF; }, [&] { - INC_COUNTER("Normal-EF Cache Hit", 1, Full); + NormalEF_CacheHit++; PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); }); PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << Ret); @@ -319,7 +319,6 @@ class FlowEdgeFunctionCache { assertNotNull(CallSite); assertNotNull(DestinationFunction); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Call edge function factory call"); PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " << NToString(CallSite)); @@ -332,13 +331,13 @@ class FlowEdgeFunctionCache { auto [It, Inserted] = CallEdgeFunctionCache.try_emplace(std::move(Key)); if (Inserted) { - INC_COUNTER("Call-EF Construction", 1, Full); + CallEF_Construction++; It->second = Problem.getCallEdgeFunction(CallSite, SrcNode, DestinationFunction, DestNode); PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); } else { - INC_COUNTER("Call-EF Cache Hit", 1, Full); + CallEF_CacheHit++; PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); } @@ -354,7 +353,6 @@ class FlowEdgeFunctionCache { assertNotNull(ExitInst); assertNotNull(RetSite); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Return edge function factory call"); PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); @@ -369,10 +367,13 @@ class FlowEdgeFunctionCache { auto [It, Inserted] = ReturnEdgeFunctionCache.try_emplace(std::move(Key)); if (Inserted) { - INC_COUNTER("Return-EF Construction", 1, Full); + ReturnEF_Construction++; It->second = Problem.getReturnEdgeFunction( CallSite, CalleeFunction, ExitInst, ExitNode, RetSite, RetNode); PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + } else { + ReturnEF_CacheHit++; + PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); } PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << It->second); @@ -386,7 +387,6 @@ class FlowEdgeFunctionCache { assertNotNull(RetSite); assertAllNotNull(Callees); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Call-to-Return edge function factory call"); @@ -406,14 +406,14 @@ class FlowEdgeFunctionCache { auto Ret = Outer.getOrInsertLazy( std::move(createEdgeFunctionNodeKey(CallNode, RetSiteNode)), [&] { - INC_COUNTER("CallToRet-EF Construction", 1, Full); + CallToRetEF_Construction++; auto Ret = Problem.getCallToRetEdgeFunction( CallSite, CallNode, RetSite, RetSiteNode, Callees); PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); return Ret; }, [&] { - INC_COUNTER("CallToRet-EF Cache Hit", 1, Full); + CallToRetEF_CacheHit++; PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); }); PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << Ret); @@ -427,7 +427,6 @@ class FlowEdgeFunctionCache { assertNotNull(CallSite); assertNotNull(RetSite); - PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Summary edge function factory call"); PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); @@ -438,12 +437,12 @@ class FlowEdgeFunctionCache { auto Key = std::tie(CallSite, CallNode, RetSite, RetSiteNode); auto [It, Inserted] = SummaryEdgeFunctionCache.try_emplace(std::move(Key)); if (Inserted) { - INC_COUNTER("Summary-EF Construction", 1, Full); + SummaryEF_Construction++; It->second = Problem.getSummaryEdgeFunction(CallSite, CallNode, RetSite, RetSiteNode); PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); } else { - INC_COUNTER("Summary-EF Cache Hit", 1, Full); + SummaryEF_CacheHit++; PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); } @@ -456,66 +455,65 @@ class FlowEdgeFunctionCache { PAMM_GET_INSTANCE; PHASAR_LOG_LEVEL(INFO, "=== Flow-Edge-Function Cache Statistics ==="); PHASAR_LOG_LEVEL(INFO, "Normal-flow function cache hits: " - << GET_COUNTER("Normal-FF Cache Hit")); + << NormalFF_CacheHit.value()); PHASAR_LOG_LEVEL(INFO, "Normal-flow function constructions: " - << GET_COUNTER("Normal-FF Construction")); - PHASAR_LOG_LEVEL(INFO, "Call-flow function cache hits: " - << GET_COUNTER("Call-FF Cache Hit")); + << NormalFF_Construction.value()); + PHASAR_LOG_LEVEL( + INFO, "Call-flow function cache hits: " << CallFF_CacheHit.value()); PHASAR_LOG_LEVEL(INFO, "Call-flow function constructions: " - << GET_COUNTER("Call-FF Construction")); + << CallFF_Construction.value()); PHASAR_LOG_LEVEL(INFO, "Return-flow function cache hits: " - << GET_COUNTER("Return-FF Cache Hit")); + << ReturnFF_CacheHit.value()); PHASAR_LOG_LEVEL(INFO, "Return-flow function constructions: " - << GET_COUNTER("Return-FF Construction")); + << ReturnFF_Construction.value()); PHASAR_LOG_LEVEL(INFO, "Call-to-Return-flow function cache hits: " - << GET_COUNTER("CallToRet-FF Cache Hit")); + << CallToRetFF_CacheHit.value()); PHASAR_LOG_LEVEL(INFO, "Call-to-Return-flow function constructions: " - << GET_COUNTER("CallToRet-FF Construction")); + << CallToRetFF_Construction.value()); PHASAR_LOG_LEVEL(INFO, "Summary-flow function cache hits: " - << GET_COUNTER("Summary-FF Cache Hit")); + << SummaryFF_CacheHit.value()); PHASAR_LOG_LEVEL(INFO, "Summary-flow function constructions: " - << GET_COUNTER("Summary-FF Construction")); - PHASAR_LOG_LEVEL(INFO, - "Total flow function cache hits: " << GET_SUM_COUNT( - {"Normal-FF Cache Hit", "Call-FF Cache Hit", - "Return-FF Cache Hit", "CallToRet-FF Cache Hit"})); + << SummaryFF_Construction.value()); + PHASAR_LOG_LEVEL( + INFO, "Total flow function cache hits: " << GET_SUM_COUNT( + NormalFF_CacheHit, CallFF_CacheHit, ReturnFF_CacheHit, + CallToRetFF_CacheHit, SummaryFF_CacheHit)); //"Summary-FF Cache Hit"}); - PHASAR_LOG_LEVEL(INFO, "Total flow function constructions: " - << GET_SUM_COUNT({"Normal-FF Construction", "Call-FF Construction", - "Return-FF Construction", - "CallToRet-FF Construction" /*, - "Summary-FF Construction"*/})); + PHASAR_LOG_LEVEL(INFO, + "Total flow function constructions: " << GET_SUM_COUNT( + NormalFF_Construction, CallFF_Construction, + CallToRetFF_Construction, ReturnFF_Construction, + SummaryFF_Construction)); PHASAR_LOG_LEVEL(INFO, ' '); - PHASAR_LOG_LEVEL(INFO, "Normal edge function cache hits: " - << GET_COUNTER("Normal-EF Cache Hit")); + PHASAR_LOG_LEVEL( + INFO, "Normal edge function cache hits: " << NormalEF_CacheHit); PHASAR_LOG_LEVEL(INFO, "Normal edge function constructions: " - << GET_COUNTER("Normal-EF Construction")); - PHASAR_LOG_LEVEL(INFO, "Call edge function cache hits: " - << GET_COUNTER("Call-EF Cache Hit")); - PHASAR_LOG_LEVEL(INFO, "Call edge function constructions: " - << GET_COUNTER("Call-EF Construction")); - PHASAR_LOG_LEVEL(INFO, "Return edge function cache hits: " - << GET_COUNTER("Return-EF Cache Hit")); + << NormalEF_Construction); + PHASAR_LOG_LEVEL(INFO, + "Call edge function cache hits: " << CallEF_CacheHit); + PHASAR_LOG_LEVEL( + INFO, "Call edge function constructions: " << CallEF_Construction); + PHASAR_LOG_LEVEL( + INFO, "Return edge function cache hits: " << ReturnEF_CacheHit); PHASAR_LOG_LEVEL(INFO, "Return edge function constructions: " - << GET_COUNTER("Return-EF Construction")); + << ReturnEF_Construction); PHASAR_LOG_LEVEL(INFO, "Call-to-Return edge function cache hits: " - << GET_COUNTER("CallToRet-EF Cache Hit")); + << CallToRetEF_CacheHit); PHASAR_LOG_LEVEL(INFO, "Call-to-Return edge function constructions: " - << GET_COUNTER("CallToRet-EF Construction")); - PHASAR_LOG_LEVEL(INFO, "Summary edge function cache hits: " - << GET_COUNTER("Summary-EF Cache Hit")); + << CallToRetEF_Construction); + PHASAR_LOG_LEVEL( + INFO, "Summary edge function cache hits: " << SummaryEF_CacheHit); PHASAR_LOG_LEVEL(INFO, "Summary edge function constructions: " - << GET_COUNTER("Summary-EF Construction")); - PHASAR_LOG_LEVEL(INFO, - "Total edge function cache hits: " << GET_SUM_COUNT( - {"Normal-EF Cache Hit", "Call-EF Cache Hit", - "Return-EF Cache Hit", "CallToRet-EF Cache Hit", - "Summary-EF Cache Hit"})); + << SummaryEF_Construction); PHASAR_LOG_LEVEL( - INFO, "Total edge function constructions: " << GET_SUM_COUNT( - {"Normal-EF Construction", "Call-EF Construction", - "Return-EF Construction", "CallToRet-EF Construction", - "Summary-EF Construction"})); + INFO, "Total edge function cache hits: " << GET_SUM_COUNT( + NormalEF_CacheHit, CallEF_CacheHit, ReturnEF_CacheHit, + CallToRetEF_CacheHit, SummaryEF_CacheHit)); + PHASAR_LOG_LEVEL(INFO, + "Total edge function constructions: " << GET_SUM_COUNT( + NormalEF_Construction, CallEF_Construction, + ReturnEF_Construction, CallToRetEF_Construction, + SummaryEF_Construction)); PHASAR_LOG_LEVEL(INFO, "----------------------------------------------"); } else { PHASAR_LOG_LEVEL( diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index a948baf0d7..202c6b60e8 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -54,6 +54,7 @@ #include "nlohmann/json.hpp" #include +#include #include #include #include @@ -74,6 +75,27 @@ class IDESolver : public IDESolverAPIMixin> { friend IDESolverAPIMixin>; + PAMM_CATEGORY(IDESolver); + + // NOLINTBEGIN + REG_COUNTER(Genfacts, 0, Core); + REG_COUNTER(Killfacts, 0, Core); + REG_COUNTER(Summaryreuse, 0, Core); + REG_COUNTER(IntraPathEdges, 0, Core); + REG_COUNTER(InterPathEdges, 0, Core); + REG_COUNTER(FFQueries, 0, Full); + REG_COUNTER(EFQueries, 0, Full); + REG_COUNTER(ValuePropagation, 0, Full); + REG_COUNTER(ValueComputation, 0, Full); + REG_COUNTER(SpecialSummaryFF_Application, 0, Full); + REG_COUNTER(SpecialSummaryEF_Queries, 0, Full); + REG_COUNTER(JumpFnConstruction, 0, Full); + REG_COUNTER(ProcessCall, 0, Full); + REG_COUNTER(ProcessNormal, 0, Full); + REG_COUNTER(ProcessExit, 0, Full); + REG_COUNTER(Calls_getAliasSet, 0, Full); + // NOLINTEND + public: using ProblemTy = IDETabulationProblem; using container_type = typename ProblemTy::container_type; @@ -383,7 +405,7 @@ class IDESolver /// virtual void processCall(const PathEdge Edge) { PAMM_GET_INSTANCE; - INC_COUNTER("Process Call", 1, Full); + ProcessCall++; PHASAR_LOG_LEVEL(DEBUG, "Process call at target: " << NToString(Edge.getTarget())); d_t d1 = Edge.factAtSource(); @@ -400,8 +422,8 @@ class IDESolver PHASAR_LOG_LEVEL(DEBUG, " " << Callee->getName()); } PHASAR_LOG_LEVEL(DEBUG, "Possible return sites:"); - for (auto ret : ReturnSiteNs) { - PHASAR_LOG_LEVEL(DEBUG, " " << NToString(ret)); + for (auto Ret : ReturnSiteNs) { + PHASAR_LOG_LEVEL(DEBUG, " " << NToString(Ret)); } }); @@ -430,13 +452,13 @@ class IDESolver PHASAR_LOG_LEVEL(DEBUG, "Found and process special summary"); for (n_t ReturnSiteN : ReturnSiteNs) { container_type Res = computeSummaryFlowFunction(SpecialSum, d1, d2); - INC_COUNTER("SpecialSummary-FF Application", 1, Full); + SpecialSummaryFF_Application++; ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); saveEdges(n, ReturnSiteN, d2, Res, ESGEdgeKind::Summary); for (d_t d3 : Res) { auto SumEdgFnE = CachedFlowEdgeFunctions.getSummaryEdgeFunction( n, d2, ReturnSiteN, d3); - INC_COUNTER("SpecialSummary-EF Queries", 1, Full); + SpecialSummaryEF_Queries++; PHASAR_LOG_LEVEL(DEBUG, "Queried Summary Edge Function: " << SumEdgFnE); @@ -452,7 +474,7 @@ class IDESolver // compute the call-flow function auto Function = CachedFlowEdgeFunctions.getCallFlowFunction(n, SCalledProcN); - INC_COUNTER("FF Queries", 1, Full); + FFQueries++; container_type Res = computeCallFlowFunction(Function, d1, d2); ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); // for each callee's start point(s) @@ -500,7 +522,7 @@ class IDESolver // compute return-flow function auto RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction( n, SCalledProcN, eP, RetSiteN); - INC_COUNTER("FF Queries", 1, Full); + FFQueries++; const container_type ReturnedFacts = computeReturnFlowFunction( RetFunction, d3, d4, n, Container{d2}); ADD_TO_HISTOGRAM("Data-flow facts", ReturnedFacts.size(), 1, @@ -527,7 +549,7 @@ class IDESolver d5)] .push_back(f5); } - INC_COUNTER("EF Queries", 2, Full); + EFQueries++; // compose call * calleeSummary * return edge functions PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << fCalleeSummary << " * " @@ -557,7 +579,7 @@ class IDESolver for (n_t ReturnSiteN : ReturnSiteNs) { auto CallToReturnFF = CachedFlowEdgeFunctions.getCallToRetFlowFunction( n, ReturnSiteN, Callees); - INC_COUNTER("FF Queries", 1, Full); + FFQueries++; container_type ReturnFacts = computeCallToReturnFlowFunction(CallToReturnFF, d1, d2); ADD_TO_HISTOGRAM("Data-flow facts", ReturnFacts.size(), 1, Full); @@ -573,7 +595,7 @@ class IDESolver IntermediateEdgeFunctions[std::make_tuple(n, d2, ReturnSiteN, d3)] .push_back(EdgeFnE); } - INC_COUNTER("EF Queries", 1, Full); + EFQueries++; auto fPrime = IDEProblem.extend(f, EdgeFnE); PHASAR_LOG_LEVEL(DEBUG, "Compose: " << EdgeFnE << " * " << f << " = " << fPrime); @@ -590,7 +612,7 @@ class IDESolver /// virtual void processNormalFlow(PathEdge Edge) { PAMM_GET_INSTANCE; - INC_COUNTER("Process Normal", 1, Full); + ProcessNormal++; PHASAR_LOG_LEVEL( DEBUG, "Process normal at target: " << NToString(Edge.getTarget())); auto f = jumpFunction(Edge); @@ -600,13 +622,14 @@ class IDESolver for (const auto nPrime : ICF->getSuccsOf(n)) { auto FlowFunc = CachedFlowEdgeFunctions.getNormalFlowFunction(n, nPrime); - INC_COUNTER("FF Queries", 1, Full); + FFQueries++; const container_type Res = computeNormalFlowFunction(FlowFunc, d1, d2); ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); saveEdges(n, nPrime, d2, Res, ESGEdgeKind::Normal); for (const d_t &d3 : Res) { auto g = CachedFlowEdgeFunctions.getNormalEdgeFunction(n, d2, nPrime, d3); + EFQueries++; PHASAR_LOG_LEVEL(DEBUG, "Queried Normal Edge Function: " << g); auto fPrime = IDEProblem.extend(f, g); @@ -624,7 +647,7 @@ class IDESolver } PHASAR_LOG_LEVEL(DEBUG, "Compose: " << g << " * " << f << " = " << fPrime); - INC_COUNTER("EF Queries", 1, Full); + WorkList.emplace_back(PathEdge(d1, DestN, std::move(d3)), std::move(fPrime)); } @@ -632,7 +655,6 @@ class IDESolver } void propagateValueAtStart(const std::pair NAndD, n_t Stmt) { - PAMM_GET_INSTANCE; d_t Fact = NAndD.second; f_t Func = ICF->getFunctionOf(Stmt); for (const n_t CallSite : ICF->getCallsFromWithin(Func)) { @@ -646,19 +668,18 @@ class IDESolver auto fPrime = Entry.second; n_t SP = Stmt; l_t Val = val(SP, Fact); - INC_COUNTER("Value Propagation", 1, Full); + ValuePropagation++; propagateValue(CallSite, dPrime, fPrime.computeTarget(Val)); } } } void propagateValueAtCall(const std::pair NAndD, n_t Stmt) { - PAMM_GET_INSTANCE; d_t Fact = NAndD.second; for (const f_t Callee : ICF->getCalleesOfCallAt(Stmt)) { auto CallFlowFunction = CachedFlowEdgeFunctions.getCallFlowFunction(Stmt, Callee); - INC_COUNTER("FF Queries", 1, Full); + FFQueries++; for (const d_t dPrime : CallFlowFunction->computeTargets(Fact)) { auto EdgeFn = CachedFlowEdgeFunctions.getCallEdgeFunction( Stmt, Fact, Callee, dPrime); @@ -669,9 +690,9 @@ class IDESolver .push_back(EdgeFn); } } - INC_COUNTER("EF Queries", 1, Full); + EFQueries++; for (const n_t StartPoint : ICF->getStartPointsOf(Callee)) { - INC_COUNTER("Value Propagation", 1, Full); + ValuePropagation++; propagateValue(StartPoint, dPrime, EdgeFn.computeTarget(val(Stmt, Fact))); } @@ -751,8 +772,7 @@ class IDESolver // should be made a callable at some point void pathEdgeProcessingTask(PathEdge Edge) { - PAMM_GET_INSTANCE; - INC_COUNTER("JumpFn Construction", 1, Full); + JumpFnConstruction++; IF_LOG_LEVEL_ENABLED(DEBUG, { PHASAR_LOG_LEVEL( DEBUG, @@ -802,7 +822,6 @@ class IDESolver // should be made a callable at some point void valueComputationTask(const std::vector &Values) { - PAMM_GET_INSTANCE; for (n_t n : Values) { for (n_t SP : ICF->getStartPointsOf(ICF->getFunctionOf(n))) { using TableCell = typename Table>::Cell; @@ -817,7 +836,7 @@ class IDESolver setVal(n, d, IDEProblem.join(val(n, d), fPrime.computeTarget(std::move(TargetVal)))); - INC_COUNTER("Value Computation", 1, Full); + ValueComputation++; } } } @@ -882,7 +901,6 @@ class IDESolver /// Clients should only call this methods if performing synchronization on /// their own. Normally, solve() should be called instead. void submitInitialSeeds() { - PAMM_GET_INSTANCE; // Check if the initial seeds contain the zero value at every starting // point. If not, the zero value needs to be added to allow for correct // solving of the problem. @@ -914,7 +932,7 @@ class IDESolver PHASAR_LOG_LEVEL(DEBUG, "\tFact: " << DToString(Fact)); PHASAR_LOG_LEVEL(DEBUG, "\tValue: " << LToString(Value)); if (!IDEProblem.isZeroValue(Fact)) { - INC_COUNTER("Gen facts", 1, Core); + Genfacts++; } WorkList.emplace_back(PathEdge(Fact, StartPoint, Fact), EdgeIdentity{}); @@ -932,7 +950,7 @@ class IDESolver /// virtual void processExit(const PathEdge Edge) { PAMM_GET_INSTANCE; - INC_COUNTER("Process Exit", 1, Full); + ProcessExit++; PHASAR_LOG_LEVEL(DEBUG, "Process exit at target: " << NToString(Edge.getTarget())); n_t n = Edge.getTarget(); // an exit node; line 21... @@ -964,7 +982,7 @@ class IDESolver // compute return-flow function auto RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction( c, FunctionThatNeedsSummary, n, RetSiteC); - INC_COUNTER("FF Queries", 1, Full); + FFQueries++; // for each incoming-call value for (d_t d4 : Entry.second) { const container_type Targets = @@ -991,7 +1009,7 @@ class IDESolver IntermediateEdgeFunctions[std::make_tuple(n, d2, RetSiteC, d5)] .push_back(f5); } - INC_COUNTER("EF Queries", 2, Full); + EFQueries += 2; // compose call function * function * return function PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << f << " * " << f4); @@ -1043,7 +1061,7 @@ class IDESolver for (n_t RetSiteC : ICF->getReturnSitesOfCallAt(Caller)) { auto RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction( Caller, FunctionThatNeedsSummary, n, RetSiteC); - INC_COUNTER("FF Queries", 1, Full); + FFQueries++; const container_type Targets = computeReturnFlowFunction( RetFunction, d1, d2, Caller, Container{ZeroValue}); ADD_TO_HISTOGRAM("Data-flow facts", Targets.size(), 1, Full); @@ -1056,7 +1074,7 @@ class IDESolver IntermediateEdgeFunctions[std::make_tuple(n, d2, RetSiteC, d5)] .push_back(f5); } - INC_COUNTER("EF Queries", 1, Full); + EFQueries++; PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << f); propagteUnbalancedReturnFlow(RetSiteC, d5, IDEProblem.extend(f, f5), Caller); @@ -1383,9 +1401,9 @@ class IDESolver // Stores all valid facts at return site in caller context; return-site is // key std::unordered_map> ValidInCallerContext; - size_t NumGenFacts = 0; - size_t NumIntraPathEdges = 0; - size_t NumInterPathEdges = 0; + ptrdiff_t NumGenFacts = 0; + ptrdiff_t NumIntraPathEdges = 0; + ptrdiff_t NumInterPathEdges = 0; // --- Intra-procedural Path Edges --- // d1 --> d2-Set // Case 1: d1 in d2-Set @@ -1498,42 +1516,38 @@ class IDESolver PHASAR_LOG_LEVEL(DEBUG, " "); } PHASAR_LOG_LEVEL(DEBUG, "SUMMARY REUSE"); - std::size_t TotalSummaryReuse = 0; + ptrdiff_t TotalSummaryReuse = 0; for (const auto &Entry : FSummaryReuse) { PHASAR_LOG_LEVEL(DEBUG, "N1: " << NToString(Entry.first.first)); PHASAR_LOG_LEVEL(DEBUG, "D1: " << DToString(Entry.first.second)); PHASAR_LOG_LEVEL(DEBUG, "#Reuse: " << Entry.second); TotalSummaryReuse += Entry.second; } - INC_COUNTER("Gen facts", NumGenFacts, Core); - INC_COUNTER("Summary-reuse", TotalSummaryReuse, Core); - INC_COUNTER("Intra Path Edges", NumIntraPathEdges, Core); - INC_COUNTER("Inter Path Edges", NumInterPathEdges, Core); + Genfacts += NumGenFacts; + Summaryreuse += TotalSummaryReuse; + IntraPathEdges += NumIntraPathEdges; + InterPathEdges += NumInterPathEdges; PHASAR_LOG_LEVEL(INFO, "----------------------------------------------"); PHASAR_LOG_LEVEL(INFO, "=== Solver Statistics ==="); - PHASAR_LOG_LEVEL(INFO, "#Facts generated : " << GET_COUNTER("Gen facts")); - PHASAR_LOG_LEVEL(INFO, "#Facts killed : " << GET_COUNTER("Kill facts")); - PHASAR_LOG_LEVEL(INFO, - "#Summary-reuse : " << GET_COUNTER("Summary-reuse")); - PHASAR_LOG_LEVEL(INFO, - "#Intra Path Edges: " << GET_COUNTER("Intra Path Edges")); - PHASAR_LOG_LEVEL(INFO, - "#Inter Path Edges: " << GET_COUNTER("Inter Path Edges")); + PHASAR_LOG_LEVEL(INFO, "#Facts generated : " << Genfacts.value()); + PHASAR_LOG_LEVEL(INFO, "#Facts killed : " << Killfacts.value()); + PHASAR_LOG_LEVEL(INFO, "#Summary-reuse : " << Summaryreuse.value()); + PHASAR_LOG_LEVEL(INFO, "#Intra Path Edges: " << IntraPathEdges.value()); + PHASAR_LOG_LEVEL(INFO, "#Inter Path Edges: " << InterPathEdges.value()); if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::Full) { - PHASAR_LOG_LEVEL( - INFO, "Flow function query count: " << GET_COUNTER("FF Queries")); - PHASAR_LOG_LEVEL( - INFO, "Edge function query count: " << GET_COUNTER("EF Queries")); + PHASAR_LOG_LEVEL(INFO, + "Flow function query count: " << FFQueries.value()); + PHASAR_LOG_LEVEL(INFO, + "Edge function query count: " << EFQueries.value()); PHASAR_LOG_LEVEL(INFO, "Data-flow value propagation count: " - << GET_COUNTER("Value Propagation")); + << ValuePropagation.value()); PHASAR_LOG_LEVEL(INFO, "Data-flow value computation count: " - << GET_COUNTER("Value Computation")); - PHASAR_LOG_LEVEL(INFO, - "Special flow function usage count: " - << GET_COUNTER("SpecialSummary-FF Application")); + << ValueComputation.value()); + PHASAR_LOG_LEVEL(INFO, "Special flow function usage count: " + << SpecialSummaryFF_Application.value()); PHASAR_LOG_LEVEL(INFO, "Jump function construciton count: " - << GET_COUNTER("JumpFn Construction")); + << JumpFnConstruction.value()); PHASAR_LOG_LEVEL(INFO, "Phase I duration: " << PRINT_TIMER("DFA Phase I")); PHASAR_LOG_LEVEL(INFO, @@ -1776,22 +1790,7 @@ class IDESolver void doInitialize() { PAMM_GET_INSTANCE; - REG_COUNTER("Gen facts", 0, Core); - REG_COUNTER("Kill facts", 0, Core); - REG_COUNTER("Summary-reuse", 0, Core); - REG_COUNTER("Intra Path Edges", 0, Core); - REG_COUNTER("Inter Path Edges", 0, Core); - REG_COUNTER("FF Queries", 0, Full); - REG_COUNTER("EF Queries", 0, Full); - REG_COUNTER("Value Propagation", 0, Full); - REG_COUNTER("Value Computation", 0, Full); - REG_COUNTER("SpecialSummary-FF Application", 0, Full); - REG_COUNTER("SpecialSummary-EF Queries", 0, Full); - REG_COUNTER("JumpFn Construction", 0, Full); - REG_COUNTER("Process Call", 0, Full); - REG_COUNTER("Process Normal", 0, Full); - REG_COUNTER("Process Exit", 0, Full); - REG_COUNTER("[Calls] getAliasSet", 0, Full); + REG_HISTOGRAM("Data-flow facts", Full); REG_HISTOGRAM("Points-to", Full); diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 99abb1a897..3a3a2a3488 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -17,15 +17,25 @@ #ifndef PHASAR_UTILS_PAMM_H_ #define PHASAR_UTILS_PAMM_H_ +#include "phasar/Utils/TemplateString.h" + #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" #include // high_resolution_clock::time_point, milliseconds +#include +#include #include #include -#include // set +#include // set +#include #include // string +#include #include // vector namespace llvm { @@ -34,6 +44,136 @@ class raw_ostream; namespace psr { +namespace pamm { + +class Registry; + +template struct Category { + constexpr operator llvm::StringRef() const noexcept { return Name; } +}; + +namespace detail { +struct CounterBase { + ptrdiff_t Ctr{}; + std::source_location Loc{}; + // TODO: Add thread-safe counter +}; +} // namespace detail + +class Registry; + +template +class Counter : private detail::CounterBase { + friend Registry; + +public: + inline explicit Counter( + std::source_location Loc = std::source_location::current()) noexcept; + + constexpr void operator++() noexcept { ++Ctr; } + constexpr void operator++(int) noexcept { ++Ctr; } + constexpr void operator+=(ptrdiff_t Offset) noexcept { Ctr += Offset; } + constexpr void operator-=(ptrdiff_t Offset) noexcept { Ctr -= Offset; } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Counter C) { + return OS << Cat << "::" << Name << ": " << C.Ctr; + } + + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(llvm::StringRef(Cat)); + Ret += llvm::StringRef(Name); + return Ret; + } + + [[nodiscard]] constexpr ptrdiff_t value() const noexcept { return Ctr; } + +private: + [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } +}; + +template class Counter { +public: + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++() noexcept {} + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++(int) noexcept {} + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void + operator+=(ptrdiff_t Offset) noexcept {} + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void + operator-=(ptrdiff_t Offset) noexcept {} + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Counter /*C*/) { + return OS << Cat << "::" << Name; + } + + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(llvm::StringRef(Cat)); + Ret += llvm::StringRef(Name); + return Ret; + } + + [[nodiscard]] constexpr std::nullopt_t value() const noexcept { + return std::nullopt; + } +}; + +namespace detail { +struct IsCounterImpl { + template + static std::true_type test(Counter); + + static std::false_type test(...); +}; + +template +concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; +} // namespace detail + +class Registry { + template + friend class Counter; + +public: + static Registry &instance() { + static Registry Reg; + return Reg; + } + + void printCounters(llvm::raw_ostream &OS) const; + +private: + template + void registerCounter( + Counter *Ctr, + std::source_location Loc = std::source_location::current()) noexcept { + assert(Ctr != nullptr); + auto [It, Inserted] = Counters[llvm::StringRef(Cat)].try_emplace( + llvm::StringRef(Name), Ctr->base()); + if (!Inserted) [[unlikely]] { + llvm::report_fatal_error( + "At " + llvm::Twine(Loc.file_name()) + ":" + llvm::Twine(Loc.line()) + + ":" + llvm::Twine(Loc.column()) + ": Counter " + + llvm::Twine(Ctr->qualifier()) + + " already registered! Previous definition was here: " + + llvm::Twine(It->second->Loc.file_name()) + ":" + + llvm::Twine(It->second->Loc.line()) + ":" + + llvm::Twine(It->second->Loc.column())); + } + } + + llvm::DenseMap> + Counters; +}; + +template +inline Counter::Counter(std::source_location Loc) noexcept { + this->Loc = Loc; + Registry::instance().registerCounter(this, Loc); +} + +} // namespace pamm + +inline constexpr pamm::Category<""> PAMMCategory{}; + /// This class offers functionality to measure different performance metrics. /// All relevant functions are wrapped into preprocessor macros and should only /// be used through those macros. Using these macros allows us to disable PAMM @@ -160,6 +300,12 @@ class PAMM final { /// \param CounterIds Unique counter ids. /// \note Macro uses variadic parameters, e.g. GET_SUM_COUNT({"foo", "bar"}). std::optional getSumCount(const std::set &CounterIds); + + [[nodiscard]] ptrdiff_t + getSumCount(pamm::detail::IsCounter auto const &...Counters) { + return (Counters.value() + ...); + } + std::optional getSumCount(llvm::ArrayRef CounterIds); std::optional diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index f7029f5c39..de1ec7ccd3 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -39,6 +39,15 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = } // namespace psr +#define PAMM_CATEGORY(NAME) \ + static inline ::psr::pamm::Category<#NAME> PAMMCategory {} + +#define REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL) \ + static inline ::psr::pamm::Counter= \ + PAMM_SEVERITY_LEVEL::SEV_LVL, \ + #COUNTER_ID, PAMMCategory> \ + COUNTER_ID + #if defined(PAMM_FULL) || defined(PAMM_CORE) // Only include PAMM header if it is used #include "phasar/Utils/PAMM.h" @@ -65,21 +74,6 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = #define PRINT_TIMER(TIMER_ID) \ pamm.getPrintableDuration(pamm.elapsedTime(TIMER_ID)) -#define REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ - pamm.regCounter(COUNTER_ID, INIT_VALUE); \ - } -#define INC_COUNTER(COUNTER_ID, VALUE, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ - static uint64_t &PammCounterRef = pamm.getOrCreateCounterRef(COUNTER_ID); \ - PammCounterRef += (VALUE); \ - } -#define DEC_COUNTER(COUNTER_ID, VALUE, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ - static uint64_t &PammCounterRef = pamm.getOrCreateCounterRef(COUNTER_ID); \ - PammCounterRef -= (VALUE); \ - } -#define GET_COUNTER(COUNTER_ID) pamm.getCounter(COUNTER_ID) #define GET_SUM_COUNT(...) pamm.getSumCount(__VA_ARGS__) #define REG_HISTOGRAM(HISTOGRAM_ID, SEV_LVL) \ @@ -103,9 +97,6 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = #define RESET_TIMER(TIMER_ID, SEV_LVL) #define PAUSE_TIMER(TIMER_ID, SEV_LVL) #define STOP_TIMER(TIMER_ID, SEV_LVL) -#define REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL) -#define INC_COUNTER(COUNTER_ID, VALUE, SEV_LVL) -#define DEC_COUNTER(COUNTER_ID, VALUE, SEV_LVL) #define REG_HISTOGRAM(HISTOGRAM_ID, SEV_LVL) #define ADD_TO_HISTOGRAM(HISTOGRAM_ID, DATAPOINT_ID, DATAPOINT_VALUE, SEV_LVL) #define PRINT_MEASURED_DATA(OUTPUT_STREAM) @@ -113,7 +104,6 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = // The following macros could be used in log messages, thus they have to // provide some default value to avoid compiler errors #define PRINT_TIMER(TIMER_ID) "" -#define GET_COUNTER(COUNTER_ID) "" #define GET_SUM_COUNT(...) "" #endif diff --git a/include/phasar/Utils/TemplateString.h b/include/phasar/Utils/TemplateString.h new file mode 100644 index 0000000000..9841d9984a --- /dev/null +++ b/include/phasar/Utils/TemplateString.h @@ -0,0 +1,21 @@ +#pragma once + +#include "llvm/ADT/StringRef.h" + +#include + +namespace psr { +template struct TemplateString { + char Buf[N + 1]{}; + + constexpr TemplateString(const char (&Str)[N + 1]) noexcept { + static_assert(N != SIZE_MAX); + for (size_t I = 0; I != N; ++I) { + Buf[I] = Str[I]; + } + } + + constexpr operator llvm::StringRef() const noexcept { return {Buf, N}; } +}; +template TemplateString(const char (&)[N]) -> TemplateString; +} // namespace psr diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp index 0f7f71e15a..a2a57ffaa2 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp @@ -22,6 +22,14 @@ namespace { using namespace psr; + +PAMM_CATEGORY(CallGraphBuilder); + +REG_COUNTER(CGFunctions, CGBuilder.viewCallGraph().getNumVertexFunctions(), + Full); +REG_COUNTER(CGCallSites, CGBuilder.viewCallGraph().getNumVertexCallSites(), + Full); + struct Builder { const LLVMProjectIRDB *IRDB = nullptr; Resolver *Res = nullptr; @@ -93,11 +101,6 @@ auto Builder::buildCallGraph(Soundness S) -> LLVMBasedCallGraph { } } - PAMM_GET_INSTANCE; - REG_COUNTER("CG Functions", CGBuilder.viewCallGraph().getNumVertexFunctions(), - Full); - REG_COUNTER("CG CallSites", CGBuilder.viewCallGraph().getNumVertexCallSites(), - Full); PHASAR_LOG_LEVEL_CAT(INFO, "LLVMBasedICFG", "Call graph has been constructed"); return CGBuilder.consumeCallGraph(); @@ -142,6 +145,8 @@ bool Builder::processFunction(const llvm::Function *F) { return true; } + ++CGFunctions; + assert(Res != nullptr); // add a node for function F to the call graph (if not present already) @@ -157,6 +162,8 @@ bool Builder::processFunction(const llvm::Function *F) { continue; } + ++CGCallSites; + FixpointReached &= fillPossibleTargets(PossibleTargets, *Res, CS, IndirectCalls); diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp index ecc95e0fa5..fe04abb343 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp @@ -31,7 +31,12 @@ #include #include -namespace psr { +using namespace psr; + +namespace { +PAMM_CATEGORY(IFDSConstAnalysis); +REG_COUNTER(Calls_getContextRelevantAliasSet, 0, Full); // NOLINT +} // namespace IFDSConstAnalysis::IFDSConstAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, @@ -41,7 +46,6 @@ IFDSConstAnalysis::IFDSConstAnalysis(const LLVMProjectIRDB *IRDB, assert(PT); PAMM_GET_INSTANCE; REG_HISTOGRAM("Context-relevant Pointer", Full); - REG_COUNTER("[Calls] getContextRelevantAliasSet", 0, Full); } IFDSConstAnalysis::FlowFunctionPtrType @@ -192,7 +196,7 @@ std::set IFDSConstAnalysis::getContextRelevantAliasSet( std::set &AliasSet, IFDSConstAnalysis::f_t CurrentContext) { PAMM_GET_INSTANCE; - INC_COUNTER("[Calls] getContextRelevantAliasSet", 1, Full); + Calls_getContextRelevantAliasSet++; START_TIMER("Context-Relevant-Alias-Set Computation", Full); std::set ToGenerate; for (const auto *Alias : AliasSet) { @@ -288,5 +292,3 @@ void IFDSConstAnalysis::emitTextReport( } OS << "\n===================================================\n"; } - -} // namespace psr diff --git a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp index d46bbc92dc..614ecf62e8 100644 --- a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp +++ b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp @@ -14,7 +14,6 @@ #include "phasar/Utils/AlignNum.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/NlohmannLogging.h" -#include "phasar/Utils/PAMMMacros.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Function.h" @@ -29,7 +28,7 @@ #include -namespace psr { +using namespace psr; static bool isAddressTaken(const llvm::Function &Fun) noexcept { for (const auto &Use : Fun.uses()) { @@ -188,16 +187,7 @@ GeneralStatisticsAnalysis::runOnModule(const llvm::Module &M) { // For performance reasons (and out of sheer convenience) we simply initialize // the counter with the values of the counter varibles, i.e. PAMM simply // holds the results. - PAMM_GET_INSTANCE; - REG_COUNTER("GS Instructions", Stats.Instructions, Core); - REG_COUNTER("GS Allocated Types", Stats.AllocatedTypes.size(), Full); - REG_COUNTER("GS Basic Blocks", Stats.BasicBlocks, Full); - REG_COUNTER("GS Call-Sites", Stats.CallSites, Full); - REG_COUNTER("GS Functions", Stats.Functions, Full); - REG_COUNTER("GS Globals", Stats.Globals, Full); - REG_COUNTER("GS Memory Intrinsics", Stats.MemIntrinsics, Full); - REG_COUNTER("GS Store Instructions", Stats.StoreInstructions, Full); - REG_COUNTER("GS Load Instructions", Stats.LoadInstructions, Full); + // Using the logging guard explicitly since we are printing allocated types // manually IF_LOG_LEVEL_ENABLED(INFO, { @@ -269,8 +259,6 @@ void GeneralStatistics::printAsJson(llvm::raw_ostream &OS) const { OS << Json << '\n'; } -} // namespace psr - llvm::raw_ostream &psr::operator<<(llvm::raw_ostream &OS, const GeneralStatistics &Statistics) { return OS diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 3cff9ae76e..63df0cd83d 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -293,19 +293,26 @@ void PAMM::printTimers(llvm::raw_ostream &OS) { } } -void PAMM::printCounters(llvm::raw_ostream &OS) { - OS << "\nCounter\n"; - OS << "-------\n"; - for (const auto &Counter : Counter) { - OS << Counter.first() << " : " << Counter.second << '\n'; +void pamm::Registry::printCounters(llvm::raw_ostream &OS) const { + OS << "\nCounters\n"; + OS << "--------\n"; + + for (const auto &[Cat, CatCtrs] : Counters) { + OS << Cat << ":\n"; + for (const auto &[Name, C] : CatCtrs) { + OS << " " << Name << ": " << C->Ctr << '\n'; + } + OS << '\n'; } - if (Counter.empty()) { + if (Counters.empty()) { OS << "No Counter registered!\n"; - } else { - OS << "\n"; } } +void PAMM::printCounters(llvm::raw_ostream &OS) { + pamm::Registry::instance().printCounters(OS); +} + void PAMM::printHistograms(llvm::raw_ostream &OS) { OS << "\nHistograms\n"; OS << "--------------\n"; From 7920c65124ce56f24873d62ea59c9e85e530ff5d Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 17 Jun 2026 18:16:53 +0200 Subject: [PATCH 03/18] Make it possible to en/disable PAMM categories selectively --- include/phasar/Utils/PAMM.h | 73 ++++++++++++++++++++++++------- include/phasar/Utils/PAMMMacros.h | 8 ++-- lib/Utils/PAMM.cpp | 5 ++- 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 3a3a2a3488..d4c0da9ace 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -17,6 +17,7 @@ #ifndef PHASAR_UTILS_PAMM_H_ #define PHASAR_UTILS_PAMM_H_ +#include "phasar/Config/phasar-config.h" #include "phasar/Utils/TemplateString.h" #include "llvm/ADT/ArrayRef.h" @@ -26,15 +27,16 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/WithColor.h" #include // high_resolution_clock::time_point, milliseconds -#include #include #include #include #include // set #include #include // string +#include #include #include // vector @@ -47,14 +49,53 @@ namespace psr { namespace pamm { class Registry; +class Category { +public: + explicit constexpr Category(llvm::StringLiteral Name, + bool IsEnabled = true) noexcept + : Name(Name) +#if defined(PAMM_FULL) || defined(PAMM_CORE) + , + IsEnabled(IsEnabled) +#endif + { + } -template struct Category { constexpr operator llvm::StringRef() const noexcept { return Name; } + + [[nodiscard]] constexpr llvm::StringRef name() const noexcept { return Name; } + + [[nodiscard]] auto isEnabled() const noexcept { return IsEnabled; } + + void disable() noexcept { +#if defined(PAMM_FULL) || defined(PAMM_CORE) + IsEnabled = false; +#endif + } + + void enable() noexcept { +#if defined(PAMM_FULL) || defined(PAMM_CORE) + IsEnabled = true; +#else + llvm::WithColor::warning() + << "Cannot enable PAMM category '" << Name + << "', because PAMM is disabled at compile time\n"; +#endif + } + +private: + llvm::StringRef Name; +#if defined(PAMM_FULL) || defined(PAMM_CORE) + bool IsEnabled = true; +#else + [[no_unique_address]] std::false_type IsEnabled{}; +#endif }; namespace detail { struct CounterBase { ptrdiff_t Ctr{}; + Category *TheCategory{}; std::source_location Loc{}; // TODO: Add thread-safe counter }; @@ -62,7 +103,7 @@ struct CounterBase { class Registry; -template +template class Counter : private detail::CounterBase { friend Registry; @@ -76,11 +117,11 @@ class Counter : private detail::CounterBase { constexpr void operator-=(ptrdiff_t Offset) noexcept { Ctr -= Offset; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Counter C) { - return OS << Cat << "::" << Name << ": " << C.Ctr; + return OS << Cat->name() << "::" << Name << ": " << C.Ctr; } [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(llvm::StringRef(Cat)); + auto Ret = std::string(Cat->name()); Ret += llvm::StringRef(Name); return Ret; } @@ -91,7 +132,7 @@ class Counter : private detail::CounterBase { [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } }; -template class Counter { +template class Counter { public: LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++() noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++(int) noexcept {} @@ -101,7 +142,7 @@ template class Counter { operator-=(ptrdiff_t Offset) noexcept {} friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Counter /*C*/) { - return OS << Cat << "::" << Name; + return OS << Cat->name() << "::" << Name; } [[nodiscard]] constexpr std::string qualifier() const { @@ -117,7 +158,7 @@ template class Counter { namespace detail { struct IsCounterImpl { - template + template static std::true_type test(Counter); static std::false_type test(...); @@ -128,7 +169,7 @@ concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; } // namespace detail class Registry { - template + template friend class Counter; public: @@ -140,13 +181,13 @@ class Registry { void printCounters(llvm::raw_ostream &OS) const; private: - template + template void registerCounter( Counter *Ctr, std::source_location Loc = std::source_location::current()) noexcept { assert(Ctr != nullptr); - auto [It, Inserted] = Counters[llvm::StringRef(Cat)].try_emplace( - llvm::StringRef(Name), Ctr->base()); + auto [It, Inserted] = + Counters[Cat].try_emplace(llvm::StringRef(Name), Ctr->base()); if (!Inserted) [[unlikely]] { llvm::report_fatal_error( "At " + llvm::Twine(Loc.file_name()) + ":" + llvm::Twine(Loc.line()) + @@ -159,20 +200,22 @@ class Registry { } } - llvm::DenseMap> Counters; }; -template +template inline Counter::Counter(std::source_location Loc) noexcept { + static_assert(Cat != nullptr); this->Loc = Loc; + this->TheCategory = Cat; Registry::instance().registerCounter(this, Loc); } } // namespace pamm -inline constexpr pamm::Category<""> PAMMCategory{}; +inline constexpr pamm::Category PAMMCategory{""}; /// This class offers functionality to measure different performance metrics. /// All relevant functions are wrapped into preprocessor macros and should only diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index de1ec7ccd3..137f9458d8 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -39,13 +39,15 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = } // namespace psr -#define PAMM_CATEGORY(NAME) \ - static inline ::psr::pamm::Category<#NAME> PAMMCategory {} +#define PAMM_CATEGORY(NAME, ...) \ + static inline ::psr::pamm::Category PAMMCategory { \ + #NAME __VA_OPT__(, ) __VA_ARGS__ \ + } #define REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL) \ static inline ::psr::pamm::Counter= \ PAMM_SEVERITY_LEVEL::SEV_LVL, \ - #COUNTER_ID, PAMMCategory> \ + #COUNTER_ID, &PAMMCategory> \ COUNTER_ID #if defined(PAMM_FULL) || defined(PAMM_CORE) diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 63df0cd83d..2f1b2456ca 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -298,7 +298,10 @@ void pamm::Registry::printCounters(llvm::raw_ostream &OS) const { OS << "--------\n"; for (const auto &[Cat, CatCtrs] : Counters) { - OS << Cat << ":\n"; + if (!Cat->isEnabled()) { + continue; + } + OS << *Cat << ":\n"; for (const auto &[Name, C] : CatCtrs) { OS << " " << Name << ": " << C->Ctr << '\n'; } From 03b0ec590f822863554b5e49d0a35d4f622c8ec0 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 17 Jun 2026 19:50:30 +0200 Subject: [PATCH 04/18] Rework PAMM histograms + rename REG_ macros to PAMM_ macros for better namespacing --- .../IfdsIde/Solver/FlowEdgeFunctionCache.h | 40 ++-- .../DataFlow/IfdsIde/Solver/IDESolver.h | 57 +++-- include/phasar/Utils/PAMM.h | 200 ++++++++++-------- include/phasar/Utils/PAMMMacros.h | 29 +-- .../ControlFlow/LLVMBasedCallGraphBuilder.cpp | 8 +- .../IfdsIde/Problems/IFDSConstAnalysis.cpp | 8 +- lib/Utils/PAMM.cpp | 127 ++--------- 7 files changed, 193 insertions(+), 276 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index ac5ab229da..85bc3cc0da 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -93,35 +93,35 @@ class FlowEdgeFunctionCache { PAMM_CATEGORY(FlowEdgeFunctionCache); // NOLINTBEGIN - REG_COUNTER(NormalFF_Construction, 0, Full); - REG_COUNTER(NormalFF_CacheHit, 0, Full); + PAMM_COUNTER(NormalFF_Construction, 0, Full); + PAMM_COUNTER(NormalFF_CacheHit, 0, Full); // Counters for the call flow functions - REG_COUNTER(CallFF_Construction, 0, Full); - REG_COUNTER(CallFF_CacheHit, 0, Full); + PAMM_COUNTER(CallFF_Construction, 0, Full); + PAMM_COUNTER(CallFF_CacheHit, 0, Full); // Counters for return flow functions - REG_COUNTER(ReturnFF_Construction, 0, Full); - REG_COUNTER(ReturnFF_CacheHit, 0, Full); + PAMM_COUNTER(ReturnFF_Construction, 0, Full); + PAMM_COUNTER(ReturnFF_CacheHit, 0, Full); // Counters for the call to return flow functions - REG_COUNTER(CallToRetFF_Construction, 0, Full); - REG_COUNTER(CallToRetFF_CacheHit, 0, Full); + PAMM_COUNTER(CallToRetFF_Construction, 0, Full); + PAMM_COUNTER(CallToRetFF_CacheHit, 0, Full); // Counters for the summary flow functions - REG_COUNTER(SummaryFF_Construction, 0, Full); - REG_COUNTER(SummaryFF_CacheHit, 0, Full); + PAMM_COUNTER(SummaryFF_Construction, 0, Full); + PAMM_COUNTER(SummaryFF_CacheHit, 0, Full); // Counters for the normal edge functions - REG_COUNTER(NormalEF_Construction, 0, Full); - REG_COUNTER(NormalEF_CacheHit, 0, Full); + PAMM_COUNTER(NormalEF_Construction, 0, Full); + PAMM_COUNTER(NormalEF_CacheHit, 0, Full); // Counters for the call edge functions - REG_COUNTER(CallEF_Construction, 0, Full); - REG_COUNTER(CallEF_CacheHit, 0, Full); + PAMM_COUNTER(CallEF_Construction, 0, Full); + PAMM_COUNTER(CallEF_CacheHit, 0, Full); // Counters for the return edge functions - REG_COUNTER(ReturnEF_Construction, 0, Full); - REG_COUNTER(ReturnEF_CacheHit, 0, Full); + PAMM_COUNTER(ReturnEF_Construction, 0, Full); + PAMM_COUNTER(ReturnEF_CacheHit, 0, Full); // Counters for the call to return edge functions - REG_COUNTER(CallToRetEF_Construction, 0, Full); - REG_COUNTER(CallToRetEF_CacheHit, 0, Full); + PAMM_COUNTER(CallToRetEF_Construction, 0, Full); + PAMM_COUNTER(CallToRetEF_CacheHit, 0, Full); // Counters for the summary edge functions - REG_COUNTER(SummaryEF_Construction, 0, Full); - REG_COUNTER(SummaryEF_CacheHit, 0, Full); + PAMM_COUNTER(SummaryEF_Construction, 0, Full); + PAMM_COUNTER(SummaryEF_CacheHit, 0, Full); // NOLINTEND diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 202c6b60e8..e36d8ad46b 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -78,22 +78,26 @@ class IDESolver PAMM_CATEGORY(IDESolver); // NOLINTBEGIN - REG_COUNTER(Genfacts, 0, Core); - REG_COUNTER(Killfacts, 0, Core); - REG_COUNTER(Summaryreuse, 0, Core); - REG_COUNTER(IntraPathEdges, 0, Core); - REG_COUNTER(InterPathEdges, 0, Core); - REG_COUNTER(FFQueries, 0, Full); - REG_COUNTER(EFQueries, 0, Full); - REG_COUNTER(ValuePropagation, 0, Full); - REG_COUNTER(ValueComputation, 0, Full); - REG_COUNTER(SpecialSummaryFF_Application, 0, Full); - REG_COUNTER(SpecialSummaryEF_Queries, 0, Full); - REG_COUNTER(JumpFnConstruction, 0, Full); - REG_COUNTER(ProcessCall, 0, Full); - REG_COUNTER(ProcessNormal, 0, Full); - REG_COUNTER(ProcessExit, 0, Full); - REG_COUNTER(Calls_getAliasSet, 0, Full); + PAMM_COUNTER(Genfacts, 0, Core); + PAMM_COUNTER(Killfacts, 0, Core); + PAMM_COUNTER(Summaryreuse, 0, Core); + PAMM_COUNTER(IntraPathEdges, 0, Core); + PAMM_COUNTER(InterPathEdges, 0, Core); + PAMM_COUNTER(FFQueries, 0, Full); + PAMM_COUNTER(EFQueries, 0, Full); + PAMM_COUNTER(ValuePropagation, 0, Full); + PAMM_COUNTER(ValueComputation, 0, Full); + PAMM_COUNTER(SpecialSummaryFF_Application, 0, Full); + PAMM_COUNTER(SpecialSummaryEF_Queries, 0, Full); + PAMM_COUNTER(JumpFnConstruction, 0, Full); + PAMM_COUNTER(ProcessCall, 0, Full); + PAMM_COUNTER(ProcessNormal, 0, Full); + PAMM_COUNTER(ProcessExit, 0, Full); + PAMM_COUNTER(Calls_getAliasSet, 0, Full); + + PAMM_HISTOGRAM(DataFlowFacts, Full); + PAMM_HISTOGRAM(PointsTo, Full); + // NOLINTEND public: @@ -404,7 +408,6 @@ class IDESolver /// @param edge an edge whose target node resembles a method call /// virtual void processCall(const PathEdge Edge) { - PAMM_GET_INSTANCE; ProcessCall++; PHASAR_LOG_LEVEL(DEBUG, "Process call at target: " << NToString(Edge.getTarget())); @@ -453,7 +456,7 @@ class IDESolver for (n_t ReturnSiteN : ReturnSiteNs) { container_type Res = computeSummaryFlowFunction(SpecialSum, d1, d2); SpecialSummaryFF_Application++; - ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); + DataFlowFacts.add(Res.size(), 1); saveEdges(n, ReturnSiteN, d2, Res, ESGEdgeKind::Summary); for (d_t d3 : Res) { auto SumEdgFnE = CachedFlowEdgeFunctions.getSummaryEdgeFunction( @@ -476,7 +479,7 @@ class IDESolver CachedFlowEdgeFunctions.getCallFlowFunction(n, SCalledProcN); FFQueries++; container_type Res = computeCallFlowFunction(Function, d1, d2); - ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); + DataFlowFacts.add(Res.size(), 1); // for each callee's start point(s) auto StartPointsOf = ICF->getStartPointsOf(SCalledProcN); if (StartPointsOf.empty()) { @@ -525,8 +528,7 @@ class IDESolver FFQueries++; const container_type ReturnedFacts = computeReturnFlowFunction( RetFunction, d3, d4, n, Container{d2}); - ADD_TO_HISTOGRAM("Data-flow facts", ReturnedFacts.size(), 1, - Full); + DataFlowFacts.add(ReturnedFacts.size(), 1); saveEdges(eP, RetSiteN, d4, ReturnedFacts, ESGEdgeKind::Ret); // for each target value of the function for (d_t d5 : ReturnedFacts) { @@ -582,7 +584,7 @@ class IDESolver FFQueries++; container_type ReturnFacts = computeCallToReturnFlowFunction(CallToReturnFF, d1, d2); - ADD_TO_HISTOGRAM("Data-flow facts", ReturnFacts.size(), 1, Full); + DataFlowFacts.add(ReturnFacts.size(), 1); saveEdges(n, ReturnSiteN, d2, ReturnFacts, HasNoCalleeInformation ? ESGEdgeKind::SkipUnknownFn : ESGEdgeKind::CallToRet); @@ -611,7 +613,6 @@ class IDESolver /// @param edge /// virtual void processNormalFlow(PathEdge Edge) { - PAMM_GET_INSTANCE; ProcessNormal++; PHASAR_LOG_LEVEL( DEBUG, "Process normal at target: " << NToString(Edge.getTarget())); @@ -624,7 +625,7 @@ class IDESolver auto FlowFunc = CachedFlowEdgeFunctions.getNormalFlowFunction(n, nPrime); FFQueries++; const container_type Res = computeNormalFlowFunction(FlowFunc, d1, d2); - ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); + DataFlowFacts.add(Res.size(), 1); saveEdges(n, nPrime, d2, Res, ESGEdgeKind::Normal); for (const d_t &d3 : Res) { auto g = @@ -949,7 +950,6 @@ class IDESolver /// @param edge an edge whose target node resembles a method exit /// virtual void processExit(const PathEdge Edge) { - PAMM_GET_INSTANCE; ProcessExit++; PHASAR_LOG_LEVEL(DEBUG, "Process exit at target: " << NToString(Edge.getTarget())); @@ -987,7 +987,7 @@ class IDESolver for (d_t d4 : Entry.second) { const container_type Targets = computeReturnFlowFunction(RetFunction, d1, d2, c, Entry.second); - ADD_TO_HISTOGRAM("Data-flow facts", Targets.size(), 1, Full); + DataFlowFacts.add(Targets.size(), 1); saveEdges(n, RetSiteC, d2, Targets, ESGEdgeKind::Ret); // for each target value at the return site // line 23 @@ -1064,7 +1064,7 @@ class IDESolver FFQueries++; const container_type Targets = computeReturnFlowFunction( RetFunction, d1, d2, Caller, Container{ZeroValue}); - ADD_TO_HISTOGRAM("Data-flow facts", Targets.size(), 1, Full); + DataFlowFacts.add(Targets.size(), 1); saveEdges(n, RetSiteC, d2, Targets, ESGEdgeKind::Ret); for (d_t d5 : Targets) { auto f5 = CachedFlowEdgeFunctions.getReturnEdgeFunction( @@ -1791,9 +1791,6 @@ class IDESolver void doInitialize() { PAMM_GET_INSTANCE; - REG_HISTOGRAM("Data-flow facts", Full); - REG_HISTOGRAM("Points-to", Full); - PHASAR_LOG_LEVEL(INFO, "IDE solver is solving the specified problem"); PHASAR_LOG_LEVEL(INFO, "Submit initial seeds, construct exploded super graph"); diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index d4c0da9ace..15ca870c24 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -19,8 +19,8 @@ #include "phasar/Config/phasar-config.h" #include "phasar/Utils/TemplateString.h" +#include "phasar/Utils/TypeTraits.h" -#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -30,10 +30,9 @@ #include "llvm/Support/WithColor.h" #include // high_resolution_clock::time_point, milliseconds +#include #include -#include #include -#include // set #include #include // string #include @@ -93,12 +92,18 @@ class Category { }; namespace detail { + struct CounterBase { ptrdiff_t Ctr{}; Category *TheCategory{}; std::source_location Loc{}; // TODO: Add thread-safe counter }; +struct HistogramBase { + llvm::StringMap HistData{}; + Category *TheCategory{}; + std::source_location Loc{}; +}; } // namespace detail class Registry; @@ -146,7 +151,7 @@ template class Counter { } [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(llvm::StringRef(Cat)); + auto Ret = std::string(Cat->name()); Ret += llvm::StringRef(Name); return Ret; } @@ -168,9 +173,74 @@ template concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; } // namespace detail +template +class Histogram : private detail::HistogramBase { + friend Registry; + +public: + inline explicit Histogram( + std::source_location Loc = std::source_location::current()) noexcept; + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const Histogram &H) { + OS << Cat->name() << "::" << Name << ":\n"; + OS << "Value\t| #Occurrences\n"; + for (const auto &[Dat, Val] : H.HistData) { + OS << Dat << "\t| " << Val << '\n'; + } + return OS; + } + + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(Cat->name()); + Ret += llvm::StringRef(Name); + return Ret; + } + + void add(llvm::StringRef DataPointId, uint64_t Increment) { + this->HistData[DataPointId] += Increment; + } + template + requires(!std::convertible_to) + void add(T &&DataPointId, uint64_t Increment) { + this->HistData[psr::adl_to_string(PSR_FWD(DataPointId))] += Increment; + } + +private: + [[nodiscard]] constexpr detail::HistogramBase *base() noexcept { + return this; + } +}; + +template +class Histogram { + friend Registry; + +public: + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Histogram /*H*/) { + return OS << Cat->name() << "::" << Name << "\n"; + } + + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(Cat->name()); + Ret += llvm::StringRef(Name); + return Ret; + } + + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void + add(llvm::StringRef /*DataPointId*/, uint64_t /*Increment*/) {} + + template + requires(!std::convertible_to) + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void add(T && /*DataPointId*/, + uint64_t /*Increment*/) {} +}; + class Registry { template friend class Counter; + template + friend class Histogram; public: static Registry &instance() { @@ -179,20 +249,20 @@ class Registry { } void printCounters(llvm::raw_ostream &OS) const; + void printHistograms(llvm::raw_ostream &OS) const; private: - template - void registerCounter( - Counter *Ctr, - std::source_location Loc = std::source_location::current()) noexcept { - assert(Ctr != nullptr); + static void registerImpl(auto *Elem, auto &Into, Category *Cat, + llvm::StringRef Name, llvm::StringRef ElemKind, + std::source_location Loc) { + assert(Elem != nullptr); auto [It, Inserted] = - Counters[Cat].try_emplace(llvm::StringRef(Name), Ctr->base()); + Into[Cat].try_emplace(llvm::StringRef(Name), Elem->base()); if (!Inserted) [[unlikely]] { llvm::report_fatal_error( "At " + llvm::Twine(Loc.file_name()) + ":" + llvm::Twine(Loc.line()) + - ":" + llvm::Twine(Loc.column()) + ": Counter " + - llvm::Twine(Ctr->qualifier()) + + ":" + llvm::Twine(Loc.column()) + ": " + ElemKind + " " + + llvm::Twine(Elem->qualifier()) + " already registered! Previous definition was here: " + llvm::Twine(It->second->Loc.file_name()) + ":" + llvm::Twine(It->second->Loc.line()) + ":" + @@ -200,9 +270,27 @@ class Registry { } } + template + void registerCounter( + Counter *Ctr, + std::source_location Loc = std::source_location::current()) noexcept { + registerImpl(Ctr, Counters, Cat, Name, "Counter", Loc); + } + + template + void registerHistogram( + Histogram *Hist, + std::source_location Loc = std::source_location::current()) noexcept { + registerImpl(Hist, Histograms, Cat, Name, "Histogram", Loc); + } + llvm::DenseMap> Counters; + + llvm::DenseMap> + Histograms; }; template @@ -213,6 +301,16 @@ inline Counter::Counter(std::source_location Loc) noexcept { Registry::instance().registerCounter(this, Loc); } +template +inline Histogram::Histogram( + std::source_location Loc) noexcept { + static_assert(Cat != nullptr); + this->Loc = Loc; + this->TheCategory = Cat; + llvm::errs() << "Registering histogram: " << Name << '\n'; + Registry::instance().registerHistogram(this, Loc); +} + } // namespace pamm inline constexpr pamm::Category PAMMCategory{""}; @@ -298,8 +396,8 @@ class PAMM final { elapsedTimeOfRepeatingTimer(); /// A running timer will not be stopped. The precision for time computation - /// is set to milliseconds and the output is similar to a timestamp, e.g. - /// '4h 8m 15sec 16ms'. + /// is set to microseconds and the output is of the form: HH:MM:SS:XXXXXX, + /// where XXXXXX are 6 digits of sub-seconds. /// /// Associated macro PRINT_TIMER(TIMERID) does not check PAMM's severity level /// explicitly. @@ -307,85 +405,11 @@ class PAMM final { /// \param timerId Unique timer id. [[nodiscard]] static std::string getPrintableDuration(uint64_t Duration); - /// \brief Registers a new counter under the given counter id - associated - /// macro: REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL). - /// \param CounterId Unique counter id. - void regCounter(llvm::StringRef CounterId, uint64_t IntialValue = 0); - - /// \brief Increases the count for the given counter - associated macro: - /// INC_COUNTER(COUNTER_ID, VALUE, SEV_LVL). - /// \param CounterId Unique counter id. - /// \param CValue to be added to the current counter. - void incCounter(llvm::StringRef CounterId, uint64_t CValue = 1); - - /// \brief Decreases the count for the given counter - associated macro: - /// DEC_COUNTER(COUNTER_ID, VALUE, SEV_LVL). - /// \param CounterId Unique counter id. - /// \param CValue to be subtracted from the current counter. - void decCounter(llvm::StringRef CounterId, uint64_t CValue = 1); - - /// The associated macro does not check PAMM's severity level explicitly. - /// \brief Returns the current count for the given counter - associated macro: - /// GET_COUNTER(COUNTER_ID). - /// \param CounterId Unique counter id. - std::optional getCounter(llvm::StringRef CounterId); - - /// \brief Returns a reference to the storage of the given counter, - /// registering it with value 0 if it does not exist yet. The - /// returned reference remains valid for the lifetime of this PAMM - /// instance, even if other counters are registered afterwards. - /// \param CounterId Unique counter id. - [[nodiscard]] uint64_t &getOrCreateCounterRef(llvm::StringRef CounterId); - - /// The associated macro does not check PAMM's severity level explicitly. - /// \brief Sums the counts for the given counter ids - associated macro: - /// GET_SUM_COUNT(...). - /// \param CounterIds Unique counter ids. - /// \note Macro uses variadic parameters, e.g. GET_SUM_COUNT({"foo", "bar"}). - std::optional getSumCount(const std::set &CounterIds); - [[nodiscard]] ptrdiff_t getSumCount(pamm::detail::IsCounter auto const &...Counters) { return (Counters.value() + ...); } - std::optional - getSumCount(llvm::ArrayRef CounterIds); - std::optional - getSumCount(std::initializer_list CounterIds); - - /// \brief Registers a new histogram - associated macro: - /// REG_HISTOGRAM(HISTOGRAM_ID, SEV_LVL). - /// \param HistogramId Unique hitogram id. - void regHistogram(llvm::StringRef HistogramId); - - /// \brief Adds a new observed data point to the corresponding histogram - - /// associated macro: ADD_TO_HISTOGRAM(HISTOGRAM_ID, DATAPOINT_ID, - /// DATAPOINT_VALUE, SEV_LVL). - /// \param HistogramId ID of the histogram that tracks given data points. - /// \param DataPointId ID of the given data point. - /// \param DataPointValue Value of the given data point. - void addToHistogram(llvm::StringRef HistogramId, llvm::StringRef DataPointId, - uint64_t DataPointValue = 1); - - /// \brief Adds a new observed data point directly to the given - /// histogram's bucket map, skipping the histogram-id lookup. - /// \param Hist Reference to a histogram's bucket map, as returned by - /// getOrCreateHistogramRef(). - /// \param DataPointId ID of the given data point. - /// \param DataPointValue Value of the given data point. - void addToHistogram(llvm::StringMap &Hist, - llvm::StringRef DataPointId, uint64_t DataPointValue = 1); - - /// \brief Returns a reference to the bucket map of the given - /// histogram, registering an empty histogram if it does not exist - /// yet. The returned reference remains valid for the lifetime of - /// this PAMM instance, even if other histograms are registered - /// afterwards. - /// \param HistogramId Unique histogram id. - [[nodiscard]] llvm::StringMap & - getOrCreateHistogramRef(llvm::StringRef HistogramId); - void stopAllTimers(); void printTimers(llvm::raw_ostream &OS); diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index 137f9458d8..c99b88e9e5 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -23,17 +23,13 @@ namespace psr { /// Defines the different level of severity of PAMM's performance evaluation enum class PAMM_SEVERITY_LEVEL { Off = 0, Core, Full }; // NOLINT -#if defined(PAMM_FULL) // NOLINTNEXTLINE -static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = +inline constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = +#if defined(PAMM_FULL) PAMM_SEVERITY_LEVEL::Full; #elif defined(PAMM_CORE) -// NOLINTNEXTLINE -static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = PAMM_SEVERITY_LEVEL::Core; #else -// NOLINTNEXTLINE -static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = PAMM_SEVERITY_LEVEL::Off; #endif @@ -44,12 +40,18 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = #NAME __VA_OPT__(, ) __VA_ARGS__ \ } -#define REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL) \ +#define PAMM_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL) \ static inline ::psr::pamm::Counter= \ PAMM_SEVERITY_LEVEL::SEV_LVL, \ #COUNTER_ID, &PAMMCategory> \ COUNTER_ID +#define PAMM_HISTOGRAM(HISTOGRAM_ID, SEV_LVL) \ + static inline ::psr::pamm::Histogram= \ + PAMM_SEVERITY_LEVEL::SEV_LVL, \ + #HISTOGRAM_ID, &PAMMCategory> \ + HISTOGRAM_ID + #if defined(PAMM_FULL) || defined(PAMM_CORE) // Only include PAMM header if it is used #include "phasar/Utils/PAMM.h" @@ -78,17 +80,6 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = #define GET_SUM_COUNT(...) pamm.getSumCount(__VA_ARGS__) -#define REG_HISTOGRAM(HISTOGRAM_ID, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ - pamm.regHistogram(HISTOGRAM_ID); \ - } -#define ADD_TO_HISTOGRAM(HISTOGRAM_ID, DATAPOINT_ID, DATAPOINT_VALUE, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ - static auto &PammHistRef = pamm.getOrCreateHistogramRef(HISTOGRAM_ID); \ - pamm.addToHistogram(PammHistRef, adl_to_string(DATAPOINT_ID), \ - DATAPOINT_VALUE); \ - } - #define PRINT_MEASURED_DATA(OUTPUT_STREAM) pamm.printMeasuredData(OUTPUT_STREAM) #define EXPORT_MEASURED_DATA(PATH) pamm.exportMeasuredData(PATH) @@ -99,8 +90,6 @@ static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = #define RESET_TIMER(TIMER_ID, SEV_LVL) #define PAUSE_TIMER(TIMER_ID, SEV_LVL) #define STOP_TIMER(TIMER_ID, SEV_LVL) -#define REG_HISTOGRAM(HISTOGRAM_ID, SEV_LVL) -#define ADD_TO_HISTOGRAM(HISTOGRAM_ID, DATAPOINT_ID, DATAPOINT_VALUE, SEV_LVL) #define PRINT_MEASURED_DATA(OUTPUT_STREAM) #define EXPORT_MEASURED_DATA(PATH) // The following macros could be used in log messages, thus they have to diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp index a2a57ffaa2..838222c2a1 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp @@ -25,10 +25,10 @@ using namespace psr; PAMM_CATEGORY(CallGraphBuilder); -REG_COUNTER(CGFunctions, CGBuilder.viewCallGraph().getNumVertexFunctions(), - Full); -REG_COUNTER(CGCallSites, CGBuilder.viewCallGraph().getNumVertexCallSites(), - Full); +PAMM_COUNTER(CGFunctions, CGBuilder.viewCallGraph().getNumVertexFunctions(), + Full); +PAMM_COUNTER(CGCallSites, CGBuilder.viewCallGraph().getNumVertexCallSites(), + Full); struct Builder { const LLVMProjectIRDB *IRDB = nullptr; diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp index fe04abb343..8f60e43450 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp @@ -35,7 +35,9 @@ using namespace psr; namespace { PAMM_CATEGORY(IFDSConstAnalysis); -REG_COUNTER(Calls_getContextRelevantAliasSet, 0, Full); // NOLINT +PAMM_COUNTER(Calls_getContextRelevantAliasSet, 0, Full); // NOLINT + +PAMM_HISTOGRAM(ContextRelevantPointer, Full); } // namespace IFDSConstAnalysis::IFDSConstAnalysis(const LLVMProjectIRDB *IRDB, @@ -44,8 +46,6 @@ IFDSConstAnalysis::IFDSConstAnalysis(const LLVMProjectIRDB *IRDB, : IFDSTabulationProblem(IRDB, std::move(EntryPoints), createZeroValue()), PT(PT) { assert(PT); - PAMM_GET_INSTANCE; - REG_HISTOGRAM("Context-relevant Pointer", Full); } IFDSConstAnalysis::FlowFunctionPtrType @@ -226,7 +226,7 @@ std::set IFDSConstAnalysis::getContextRelevantAliasSet( } // ignore everything else } PAUSE_TIMER("Context-Relevant-Alias-Set Computation", Full); - ADD_TO_HISTOGRAM("Context-relevant Pointer", ToGenerate.size(), 1, Full); + ContextRelevantPointer.add(ToGenerate.size(), 1); return ToGenerate; } diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 2f1b2456ca..895505e8c9 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -30,11 +30,6 @@ #include #include -#include -#include -#include -#include -#include #include using namespace psr; @@ -148,104 +143,6 @@ std::string PAMM::getPrintableDuration(uint64_t Duration) { return hms(Duration_t{Duration}).str(); } -void PAMM::regCounter(llvm::StringRef CounterId, uint64_t IntialValue) { - [[maybe_unused]] auto [It, Inserted] = - Counter.try_emplace(CounterId, IntialValue); - assert(Inserted && "regCounter failed due to an invalid counter id"); -} - -void PAMM::incCounter(llvm::StringRef CounterId, uint64_t CValue) { - auto It = Counter.find(CounterId); - bool ValidCounterId = It != Counter.end(); - assert(ValidCounterId && "incCounter failed due to an invalid counter id"); - if (ValidCounterId) { - It->second += CValue; - } -} - -void PAMM::decCounter(llvm::StringRef CounterId, uint64_t CValue) { - auto It = Counter.find(CounterId); - bool ValidCounterId = It != Counter.end(); - assert(ValidCounterId && "decCounter failed due to an invalid counter id"); - if (ValidCounterId) { - It->second -= CValue; - } -} - -std::optional PAMM::getCounter(llvm::StringRef CounterId) { - auto It = Counter.find(CounterId); - bool ValidCounterId = It != Counter.end(); - assert(ValidCounterId && "getCounter failed due to an invalid counter id"); - if (ValidCounterId) { - return It->second; - } - return std::nullopt; -} - -uint64_t &PAMM::getOrCreateCounterRef(llvm::StringRef CounterId) { - return Counter[CounterId]; -} - -template -static std::optional -getSumCountInternal(PAMM &P, ForwardIterator It, - ForwardIteratorSentinel End) noexcept { - uint64_t Ctr = 0; - for (; It != End; ++It) { - auto Count = P.getCounter(*It); - if (!Count) { - return std::nullopt; - } - - Ctr += *Count; - } - - return Ctr; -} - -std::optional -PAMM::getSumCount(const std::set &CounterIds) { - return getSumCountInternal(*this, CounterIds.begin(), CounterIds.end()); -} - -std::optional -PAMM::getSumCount(llvm::ArrayRef CounterIds) { - return getSumCountInternal(*this, CounterIds.begin(), CounterIds.end()); -} - -std::optional -PAMM::getSumCount(std::initializer_list CounterIds) { - return getSumCountInternal(*this, CounterIds.begin(), CounterIds.end()); -} - -void PAMM::regHistogram(llvm::StringRef HistogramId) { - [[maybe_unused]] auto [It, Inserted] = Histogram.try_emplace(HistogramId); - assert(Inserted && "failed to register new histogram due to an invalid id"); -} - -void PAMM::addToHistogram(llvm::StringRef HistogramId, - llvm::StringRef DataPointId, - uint64_t DataPointValue) { - auto HistIt = Histogram.find(HistogramId); - if (HistIt == Histogram.end()) { - assert(false && "adding data point to histogram failed due to invalid id"); - return; - } - - addToHistogram(HistIt->second, DataPointId, DataPointValue); -} - -void PAMM::addToHistogram(llvm::StringMap &Hist, - llvm::StringRef DataPointId, - uint64_t DataPointValue) { - Hist[DataPointId] += DataPointValue; -} - -llvm::StringMap & -PAMM::getOrCreateHistogramRef(llvm::StringRef HistogramId) { - return Histogram[HistogramId]; -} - void PAMM::stopAllTimers() { while (!RunningTimer.empty()) { // safe copy @@ -317,17 +214,27 @@ void PAMM::printCounters(llvm::raw_ostream &OS) { } void PAMM::printHistograms(llvm::raw_ostream &OS) { + pamm::Registry::instance().printHistograms(OS); +} + +void pamm::Registry::printHistograms(llvm::raw_ostream &OS) const { OS << "\nHistograms\n"; OS << "--------------\n"; - for (const auto &H : Histogram) { - OS << H.first() << " Histogram\n"; - OS << "Value : #Occurrences\n"; - for (const auto &Entry : H.second) { - OS << Entry.first() << " : " << Entry.second << '\n'; + for (const auto &[Cat, Hists] : Histograms) { + if (!Cat->isEnabled()) { + continue; + } + for (const auto &[Name, H] : Hists) { + OS << Cat->name() << "::" << Name << " Histogram:\n"; + OS << " Value\t| #Occurrences\n"; + OS << " -----\t| ------------\n"; + for (const auto &[Dat, Val] : H->HistData) { + OS << " " << Dat << "\t| " << Val << '\n'; + } + OS << '\n'; } - OS << '\n'; } - if (Histogram.empty()) { + if (Histograms.empty()) { OS << "No histograms tracked!\n"; } } From 64fb798ba1c38e770112f3189ee46ac26a8f5be9 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 18 Jun 2026 18:31:37 +0200 Subject: [PATCH 05/18] Selective printing --- include/phasar/Utils/PAMM.h | 46 +++++++-------- lib/Utils/PAMM.cpp | 115 ++++++++++++++++++++++++++++++------ 2 files changed, 119 insertions(+), 42 deletions(-) diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 15ca870c24..df675e9032 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -1,22 +1,14 @@ +#pragma once + /****************************************************************************** * Copyright (c) 2017 Philipp Schubert. * All rights reserved. This program and the accompanying materials are made * available under the terms of LICENSE.txt. * * Contributors: - * Philipp Schubert and others + * Philipp Schubert, Fabian Schiebel, and others *****************************************************************************/ -/* - * PAMM.h - * - * Created on: 06.12.2017 - * Author: rleer - */ - -#ifndef PHASAR_UTILS_PAMM_H_ -#define PHASAR_UTILS_PAMM_H_ - #include "phasar/Config/phasar-config.h" #include "phasar/Utils/TemplateString.h" #include "phasar/Utils/TypeTraits.h" @@ -64,9 +56,9 @@ class Category { [[nodiscard]] constexpr llvm::StringRef name() const noexcept { return Name; } - [[nodiscard]] auto isEnabled() const noexcept { return IsEnabled; } + [[nodiscard]] constexpr auto isEnabled() const noexcept { return IsEnabled; } - void disable() noexcept { + constexpr void disable() noexcept { #if defined(PAMM_FULL) || defined(PAMM_CORE) IsEnabled = false; #endif @@ -198,12 +190,14 @@ class Histogram : private detail::HistogramBase { } void add(llvm::StringRef DataPointId, uint64_t Increment) { - this->HistData[DataPointId] += Increment; + if (Cat->isEnabled()) { + this->HistData[DataPointId] += Increment; + } } template requires(!std::convertible_to) void add(T &&DataPointId, uint64_t Increment) { - this->HistData[psr::adl_to_string(PSR_FWD(DataPointId))] += Increment; + add(psr::adl_to_string(PSR_FWD(DataPointId)), Increment); } private: @@ -249,7 +243,15 @@ class Registry { } void printCounters(llvm::raw_ostream &OS) const; + void printCounters(llvm::raw_ostream &OS, const Category &Cat) const; void printHistograms(llvm::raw_ostream &OS) const; + void printHistograms(llvm::raw_ostream &OS, const Category &Cat) const; + + /// Performs a linear search on all registered elements to find the category + /// with the given name. If none is found, returns nullptr. + /// + /// This method can be rather expensive. + [[nodiscard]] const Category *findCategory(llvm::StringRef Name) const; private: static void registerImpl(auto *Elem, auto &Into, Category *Cat, @@ -284,11 +286,11 @@ class Registry { registerImpl(Hist, Histograms, Cat, Name, "Histogram", Loc); } - llvm::DenseMap> Counters; - llvm::DenseMap> Histograms; }; @@ -307,14 +309,11 @@ inline Histogram::Histogram( static_assert(Cat != nullptr); this->Loc = Loc; this->TheCategory = Cat; - llvm::errs() << "Registering histogram: " << Name << '\n'; Registry::instance().registerHistogram(this, Loc); } } // namespace pamm -inline constexpr pamm::Category PAMMCategory{""}; - /// This class offers functionality to measure different performance metrics. /// All relevant functions are wrapped into preprocessor macros and should only /// be used through those macros. Using these macros allows us to disable PAMM @@ -401,11 +400,9 @@ class PAMM final { /// /// Associated macro PRINT_TIMER(TIMERID) does not check PAMM's severity level /// explicitly. - /// \brief Returns the elapsed time for a given timer id. - /// \param timerId Unique timer id. [[nodiscard]] static std::string getPrintableDuration(uint64_t Duration); - [[nodiscard]] ptrdiff_t + [[nodiscard]] static ptrdiff_t getSumCount(pamm::detail::IsCounter auto const &...Counters) { return (Counters.value() + ...); } @@ -421,6 +418,7 @@ class PAMM final { /// \brief Prints the measured data to the commandline - associated macro: /// PRINT_MEASURED_DATA void printMeasuredData(llvm::raw_ostream &OS); + void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat); /// \brief Exports the measured data to JSON - associated macro: /// EXPORT_MEASURED_DATA(PATH). @@ -444,4 +442,4 @@ class PAMM final { } // namespace psr -#endif +inline constexpr psr::pamm::Category PAMMCategory{""}; diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 895505e8c9..b3a3aee5b9 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -17,6 +17,7 @@ #include "phasar/Utils/PAMM.h" #include "phasar/Utils/ChronoUtils.h" +#include "phasar/Utils/MapUtils.h" #include "phasar/Utils/NlohmannLogging.h" #include "llvm/ADT/SmallString.h" @@ -190,25 +191,48 @@ void PAMM::printTimers(llvm::raw_ostream &OS) { } } +static void printCountersImpl( + llvm::raw_ostream &OS, const pamm::Category &Cat, + const llvm::DenseMap + &CatCtrs) { + OS << Cat.name() << ":\n"; + for (const auto &[Name, C] : CatCtrs) { + OS << " " << Name << ": " << C->Ctr << '\n'; + } + OS << '\n'; +} + void pamm::Registry::printCounters(llvm::raw_ostream &OS) const { OS << "\nCounters\n"; OS << "--------\n"; for (const auto &[Cat, CatCtrs] : Counters) { - if (!Cat->isEnabled()) { - continue; + if (Cat->isEnabled()) { + printCountersImpl(OS, *Cat, CatCtrs); } - OS << *Cat << ":\n"; - for (const auto &[Name, C] : CatCtrs) { - OS << " " << Name << ": " << C->Ctr << '\n'; - } - OS << '\n'; } if (Counters.empty()) { OS << "No Counter registered!\n"; } } +void pamm::Registry::printCounters(llvm::raw_ostream &OS, + const Category &Cat) const { + if (!Cat.isEnabled()) { + OS << "Category '" << Cat.name() << "' is disabled\n"; + return; + } + const auto *CatCtrs = getOrNull(Counters, &Cat); + if (!CatCtrs || CatCtrs->empty()) { + OS << "No Counters for category '" << Cat.name() << "' registered!\n"; + return; + } + + OS << "\nCounters\n"; + OS << "--------\n"; + printCountersImpl(OS, Cat, *CatCtrs); +} + void PAMM::printCounters(llvm::raw_ostream &OS) { pamm::Registry::instance().printCounters(OS); } @@ -217,21 +241,27 @@ void PAMM::printHistograms(llvm::raw_ostream &OS) { pamm::Registry::instance().printHistograms(OS); } +static void printHistogramsImpl( + llvm::raw_ostream &OS, const pamm::Category &Cat, + const llvm::DenseMap + &Hists) { + for (const auto &[Name, H] : Hists) { + OS << Cat.name() << "::" << Name << " Histogram:\n"; + OS << " Value\t| #Occurrences\n"; + OS << " -----\t| ------------\n"; + for (const auto &[Dat, Val] : H->HistData) { + OS << " " << Dat << "\t| " << Val << '\n'; + } + OS << '\n'; + } +} + void pamm::Registry::printHistograms(llvm::raw_ostream &OS) const { OS << "\nHistograms\n"; OS << "--------------\n"; for (const auto &[Cat, Hists] : Histograms) { - if (!Cat->isEnabled()) { - continue; - } - for (const auto &[Name, H] : Hists) { - OS << Cat->name() << "::" << Name << " Histogram:\n"; - OS << " Value\t| #Occurrences\n"; - OS << " -----\t| ------------\n"; - for (const auto &[Dat, Val] : H->HistData) { - OS << " " << Dat << "\t| " << Val << '\n'; - } - OS << '\n'; + if (Cat->isEnabled()) { + printHistogramsImpl(OS, *Cat, Hists); } } if (Histograms.empty()) { @@ -239,6 +269,23 @@ void pamm::Registry::printHistograms(llvm::raw_ostream &OS) const { } } +void pamm::Registry::printHistograms(llvm::raw_ostream &OS, + const Category &Cat) const { + if (!Cat.isEnabled()) { + OS << "Category '" << Cat.name() << "' is disabled\n"; + return; + } + const auto *Hists = getOrNull(Histograms, &Cat); + if (!Hists || Hists->empty()) { + OS << "No Histograms for category '" << Cat.name() << "' registered!\n"; + return; + } + + OS << "\nHistograms\n"; + OS << "--------------\n"; + printHistogramsImpl(OS, Cat, *Hists); +} + void PAMM::printMeasuredData(llvm::raw_ostream &Os) { Os << "\n----- START OF EVALUATION DATA -----\n\n"; printTimers(Os); @@ -327,6 +374,19 @@ void PAMM::exportMeasuredData( OS << JsonData << '\n'; } +void PAMM::printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { + OS << "\n----- START OF EVALUATION DATA -----\n\n"; + if (!Cat.isEnabled()) { + OS << "Category '" << Cat.name() << "' is disabled\n"; + return; + } + auto &Reg = pamm::Registry::instance(); + // Reg.printTimers(OS); + Reg.printCounters(OS, Cat); + Reg.printHistograms(OS, Cat); + OS << "\n----- END OF EVALUATION DATA -----\n\n"; +} + void PAMM::reset() { RunningTimer.clear(); StoppedTimer.clear(); @@ -334,4 +394,23 @@ void PAMM::reset() { Counter.clear(); Histogram.clear(); } + +auto pamm::Registry::findCategory(llvm::StringRef Name) const + -> const Category * { + for (const auto &[Cat, _] : Counters) { + if (Cat->name() == Name) { + return Cat; + } + } + + for (const auto &[Cat, _] : Histograms) { + if (Cat->name() == Name) { + return Cat; + } + } + + // TODO: Timers + + return nullptr; +} } // namespace psr From c81bdf728be4226fa571046423d65d8c4e3a82ab Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 18 Jun 2026 19:27:14 +0200 Subject: [PATCH 06/18] Timers --- .../DataFlow/IfdsIde/Solver/IDESolver.h | 29 +- .../phasar/DataFlow/IfdsIde/SolverResults.h | 14 +- include/phasar/Utils/Macros.h | 3 + include/phasar/Utils/PAMM.h | 239 +++++++++------ include/phasar/Utils/PAMMMacros.h | 10 + .../IfdsIde/Problems/IFDSConstAnalysis.cpp | 9 +- lib/Utils/PAMM.cpp | 277 +++--------------- 7 files changed, 241 insertions(+), 340 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index e36d8ad46b..65705d7dab 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -98,6 +98,9 @@ class IDESolver PAMM_HISTOGRAM(DataFlowFacts, Full); PAMM_HISTOGRAM(PointsTo, Full); + PAMM_TIMER(DFAPhase1, Full); + PAMM_TIMER(DFAPhase2, Full); + // NOLINTEND public: @@ -433,12 +436,12 @@ class IDESolver bool HasNoCalleeInformation = true; auto &&Fun = ICF->getFunctionOf(n); - auto GetNextUse = [this, &Fun, &n](n_t nPrime, ByConstRef d3) { + auto GetNextUse = [this, &Fun, &n](n_t NPrime, ByConstRef d3) { if (auto &&NextUser = getNextUserOrNull(Fun, d3, n)) { return psr::unwrapNullable(PSR_FWD(NextUser)); } - return nPrime; + return NPrime; }; // for each possible callee @@ -1397,7 +1400,6 @@ class IDESolver /// generated/killed facts, number of summary-reuses etc. /// void computeAndPrintStatistics() { - PAMM_GET_INSTANCE; // Stores all valid facts at return site in caller context; return-site is // key std::unordered_map> ValidInCallerContext; @@ -1472,8 +1474,9 @@ class IDESolver std::set SummaryDSet; EndsummaryTab.get(Edge.second, D2) - .foreachCell([&SummaryDSet](const auto &Row, const auto &Col, - const auto &Val) { + .foreachCell([&SummaryDSet](const auto & /*Row*/, + const auto &Col, + const auto & /*Val*/) { SummaryDSet.insert(Col); }); @@ -1548,10 +1551,8 @@ class IDESolver << SpecialSummaryFF_Application.value()); PHASAR_LOG_LEVEL(INFO, "Jump function construciton count: " << JumpFnConstruction.value()); - PHASAR_LOG_LEVEL(INFO, - "Phase I duration: " << PRINT_TIMER("DFA Phase I")); - PHASAR_LOG_LEVEL(INFO, - "Phase II duration: " << PRINT_TIMER("DFA Phase II")); + PHASAR_LOG_LEVEL(INFO, "Phase I duration: " << DFAPhase1.elapsed()); + PHASAR_LOG_LEVEL(INFO, "Phase II duration: " << DFAPhase2.elapsed()); PHASAR_LOG_LEVEL(INFO, "----------------------------------------------"); CachedFlowEdgeFunctions.print(); } @@ -1789,13 +1790,11 @@ class IDESolver /// -- InteractiveIDESolverMixin implementation void doInitialize() { - PAMM_GET_INSTANCE; - PHASAR_LOG_LEVEL(INFO, "IDE solver is solving the specified problem"); PHASAR_LOG_LEVEL(INFO, "Submit initial seeds, construct exploded super graph"); // computations starting here - START_TIMER("DFA Phase I", Full); + DFAPhase1.start(); // We start our analysis and construct exploded supergraph submitInitialSeeds(); @@ -1817,17 +1816,15 @@ class IDESolver } void finalizeInternal() { - PAMM_GET_INSTANCE; - STOP_TIMER("DFA Phase I", Full); + DFAPhase1.stop(); PHASAR_LOG_LEVEL(INFO, "[info]: IDE Phase I completed"); if (SolverConfig.computeValues()) { - START_TIMER("DFA Phase II", Full); + PAMM_SCOPED_TIMER(DFAPhase2); // Computing the final values for the edge functions PHASAR_LOG_LEVEL( INFO, "Compute the final values according to the edge functions"); computeValues(); - STOP_TIMER("DFA Phase II", Full); } PHASAR_LOG_LEVEL(INFO, "Problem solved"); diff --git a/include/phasar/DataFlow/IfdsIde/SolverResults.h b/include/phasar/DataFlow/IfdsIde/SolverResults.h index 12cc2a9e6a..52bc985daa 100644 --- a/include/phasar/DataFlow/IfdsIde/SolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/SolverResults.h @@ -40,8 +40,15 @@ namespace psr { std::string getMetaDataID(const llvm::Value *V); namespace detail { + +struct SolverResultsRoot { + PAMM_CATEGORY(SolverResults); + PAMM_TIMER(ResultsDumping, Full); +}; + template -class SolverResultsBase { +class SolverResultsBase : private SolverResultsRoot { + public: using n_t = N; using d_t = D; @@ -160,8 +167,8 @@ class SolverResultsBase { llvm::raw_ostream &OS = llvm::outs()) const { using f_t = typename ICFGTy::f_t; - PAMM_GET_INSTANCE; - START_TIMER("DFA IDE Result Dumping", Full); + PAMM_SCOPED_TIMER(ResultsDumping); + OS << "\n***************************************************************\n" << "* Raw IDESolver results *\n" << "***************************************************************\n"; @@ -202,7 +209,6 @@ class SolverResultsBase { } } OS << '\n'; - STOP_TIMER("DFA IDE Result Dumping", Full); } template diff --git a/include/phasar/Utils/Macros.h b/include/phasar/Utils/Macros.h index 74d3c6690f..519d3e7a21 100644 --- a/include/phasar/Utils/Macros.h +++ b/include/phasar/Utils/Macros.h @@ -10,6 +10,9 @@ #ifndef PHASAR_UTILS_MACROS_H #define PHASAR_UTILS_MACROS_H +#define PSR_CONCAT_IMPL(X, Y) X##Y +#define PSR_CONCAT(X, Y) PSR_CONCAT_IMPL(X, Y) + #define PSR_FWD(...) ::std::forward(__VA_ARGS__) #define PSR_CONCEPT concept diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index df675e9032..ff091c395c 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -10,7 +10,10 @@ *****************************************************************************/ #include "phasar/Config/phasar-config.h" +#include "phasar/Utils/ChronoUtils.h" +#include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/TemplateString.h" +#include "phasar/Utils/Timer.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/DenseMap.h" @@ -84,23 +87,50 @@ class Category { }; namespace detail { - -struct CounterBase { - ptrdiff_t Ctr{}; - Category *TheCategory{}; +struct PAMMBase { + const Category *TheCategory{}; std::source_location Loc{}; +}; +struct CounterBase : PAMMBase { + ptrdiff_t Ctr{}; // TODO: Add thread-safe counter }; -struct HistogramBase { +struct HistogramBase : PAMMBase { llvm::StringMap HistData{}; - Category *TheCategory{}; - std::source_location Loc{}; +}; +struct TimerBase : PAMMBase { + std::optional Tm{}; + std::chrono::nanoseconds Acc{}; + + void start() noexcept { + assert(!Tm.has_value() && + "Starting an already running timer is not allowed"); + Tm.emplace(); + } + + void stop() noexcept { + assert(Tm.has_value() && "Stopping a not-running timer is not allowed"); + Acc += Tm->elapsedNanos(); + Tm.reset(); + } + + void reset() noexcept { + Acc = {}; + Tm.reset(); + } + + [[nodiscard]] constexpr std::chrono::nanoseconds + elapsedNanos() const noexcept { + return Acc; + } + + [[nodiscard]] hms elapsed() const noexcept { return Acc; } }; } // namespace detail class Registry; -template +template class Counter : private detail::CounterBase { friend Registry; @@ -129,7 +159,8 @@ class Counter : private detail::CounterBase { [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } }; -template class Counter { +template +class Counter { public: LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++() noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++(int) noexcept {} @@ -155,7 +186,7 @@ template class Counter { namespace detail { struct IsCounterImpl { - template + template static std::true_type test(Counter); static std::false_type test(...); @@ -165,7 +196,7 @@ template concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; } // namespace detail -template +template class Histogram : private detail::HistogramBase { friend Registry; @@ -206,7 +237,7 @@ class Histogram : private detail::HistogramBase { } }; -template +template class Histogram { friend Registry; @@ -230,11 +261,92 @@ class Histogram { uint64_t /*Increment*/) {} }; +template +class Timer : private detail::TimerBase { + friend Registry; + template friend class ScopedTimer; + +public: + inline explicit Timer( + std::source_location Loc = std::source_location::current()) noexcept; + + using detail::TimerBase::elapsed; + using detail::TimerBase::elapsedNanos; + using detail::TimerBase::reset; + using detail::TimerBase::start; + using detail::TimerBase::stop; + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Timer &T) { + return OS << Cat->name() << "::" << Name << ": " << T.elapsed(); + } + + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(Cat->name()); + Ret += llvm::StringRef(Name); + return Ret; + } + +private: + [[nodiscard]] constexpr detail::TimerBase *base() noexcept { return this; } +}; + +template +class Timer { +public: + LLVM_ATTRIBUTE_ALWAYS_INLINE void start() noexcept {} + LLVM_ATTRIBUTE_ALWAYS_INLINE void stop() noexcept {} + + [[nodiscard]] constexpr std::chrono::nanoseconds + elapsedNanos() const noexcept { + return {}; + } + + [[nodiscard]] hms elapsed() const noexcept { return {}; } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Timer /*T*/) { + return OS << Cat->name() << "::" << Name; + } + + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(Cat->name()); + Ret += llvm::StringRef(Name); + return Ret; + } +}; + +template class ScopedTimer { +public: + template + constexpr ScopedTimer(Timer &Tm) : Tm(&Tm) { + if (Cat->isEnabled()) { + Tm.start(); + } + } + + ~ScopedTimer() { Tm->stop(); } + + ScopedTimer(const ScopedTimer &) = delete; + ScopedTimer &operator=(const ScopedTimer &) = delete; + ScopedTimer(ScopedTimer &&) = delete; + ScopedTimer &operator=(ScopedTimer &&) = delete; + +private: + NonNullPtr Tm; +}; + +template <> class ScopedTimer { +public: + template + constexpr ScopedTimer(Timer &Tm) {} +}; + class Registry { - template + template friend class Counter; - template + template friend class Histogram; + template + friend class Timer; public: static Registry &instance() { @@ -244,9 +356,13 @@ class Registry { void printCounters(llvm::raw_ostream &OS) const; void printCounters(llvm::raw_ostream &OS, const Category &Cat) const; + void printHistograms(llvm::raw_ostream &OS) const; void printHistograms(llvm::raw_ostream &OS, const Category &Cat) const; + void printTimers(llvm::raw_ostream &OS) const; + void printTimers(llvm::raw_ostream &OS, const Category &Cat) const; + /// Performs a linear search on all registered elements to find the category /// with the given name. If none is found, returns nullptr. /// @@ -254,7 +370,7 @@ class Registry { [[nodiscard]] const Category *findCategory(llvm::StringRef Name) const; private: - static void registerImpl(auto *Elem, auto &Into, Category *Cat, + static void registerImpl(auto *Elem, auto &Into, const Category *Cat, llvm::StringRef Name, llvm::StringRef ElemKind, std::source_location Loc) { assert(Elem != nullptr); @@ -272,20 +388,27 @@ class Registry { } } - template + template void registerCounter( Counter *Ctr, std::source_location Loc = std::source_location::current()) noexcept { registerImpl(Ctr, Counters, Cat, Name, "Counter", Loc); } - template + template void registerHistogram( Histogram *Hist, std::source_location Loc = std::source_location::current()) noexcept { registerImpl(Hist, Histograms, Cat, Name, "Histogram", Loc); } + template + void registerTimer( + Timer *Tm, + std::source_location Loc = std::source_location::current()) noexcept { + registerImpl(Tm, Timers, Cat, Name, "Timer", Loc); + } + llvm::DenseMap> Counters; @@ -293,9 +416,13 @@ class Registry { llvm::DenseMap> Histograms; + + llvm::DenseMap> + Timers; }; -template +template inline Counter::Counter(std::source_location Loc) noexcept { static_assert(Cat != nullptr); this->Loc = Loc; @@ -303,7 +430,7 @@ inline Counter::Counter(std::source_location Loc) noexcept { Registry::instance().registerCounter(this, Loc); } -template +template inline Histogram::Histogram( std::source_location Loc) noexcept { static_assert(Cat != nullptr); @@ -312,6 +439,13 @@ inline Histogram::Histogram( Registry::instance().registerHistogram(this, Loc); } +template +inline Timer::Timer(std::source_location Loc) noexcept { + static_assert(Cat != nullptr); + this->Loc = Loc; + this->TheCategory = Cat; + Registry::instance().registerTimer(this, Loc); +} } // namespace pamm /// This class offers functionality to measure different performance metrics. @@ -351,86 +485,21 @@ class PAMM final { /// macro: PAMM_GET_INSTANCE. [[nodiscard]] static PAMM &getInstance(); - /// \brief Resets PAMM, i.e. discards all gathered information (timer, counter - /// etc.) - associated macro: RESET_PAMM. - /// \note Only used for unit testing to reset PAMM in between test runs. - void reset(); - - /// \brief Starts a timer under the given timer id - associated macro: - /// START_TIMER(TIMER_ID, SEV_LVL). - /// \param TimerId Unique timer id. - void startTimer(llvm::StringRef TimerId); - - /// \brief Resets timer under the given timer id - associated macro: - /// RESET_TIMER(TIMER_ID, SEV_LVL). - /// \param TimerId Unique timer id. - void resetTimer(llvm::StringRef TimerId); - - /// If pauseTimer is true, a running timer gets paused, its start time point - /// will paired with a current time point, and stored as an accumulated timer. - /// This enables us to repeatedly compute execution time for a certain portion - /// of code which is executed multiple times, e.g. a loop or a function - /// call, without using a different timer id for every time computation. - /// Times of all executions of one timer are saved as distinct time point - /// pairs. Associated macro: - /// PAUSE_TIMER(TIMER_ID, SEV_LVL) - /// - /// Otherwise, the timer will be simply stopped. Associated macro: - /// STOP_TIMER(TIMER_ID, SEV_LVL) - /// \brief Stops or pauses a timer under the given timer id. - /// \param TimerId Unique timer id. - /// \param PauseTimer If true, timer will be paused instead of stopped. - void stopTimer(llvm::StringRef TimerId, bool PauseTimer = false); - - /// \brief Computes the elapsed time of the given timer up until now or up to - /// the moment the timer was stopped - associated macro: GET_TIMER(TIMERID) - /// \param TimerId Unique timer id. - /// \return Timer duration. - uint64_t elapsedTime(llvm::StringRef TimerId); - - /// For each accumulated timer a vector holds all recorded durations. - /// \brief Computes the elapsed time for all accumulated timer being used. - /// \return Map containing measured durations of all accumulated timer. - [[nodiscard]] llvm::StringMap> - elapsedTimeOfRepeatingTimer(); - - /// A running timer will not be stopped. The precision for time computation - /// is set to microseconds and the output is of the form: HH:MM:SS:XXXXXX, - /// where XXXXXX are 6 digits of sub-seconds. - /// - /// Associated macro PRINT_TIMER(TIMERID) does not check PAMM's severity level - /// explicitly. - [[nodiscard]] static std::string getPrintableDuration(uint64_t Duration); - [[nodiscard]] static ptrdiff_t getSumCount(pamm::detail::IsCounter auto const &...Counters) { return (Counters.value() + ...); } - void stopAllTimers(); - void printTimers(llvm::raw_ostream &OS); void printCounters(llvm::raw_ostream &OS); void printHistograms(llvm::raw_ostream &OS); - /// \brief Prints the measured data to the commandline - associated macro: - /// PRINT_MEASURED_DATA + /// \brief Prints the measured data to the commandline void printMeasuredData(llvm::raw_ostream &OS); void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat); - /// \brief Exports the measured data to JSON - associated macro: - /// EXPORT_MEASURED_DATA(PATH). - /// \param OutputPath to exported JSON file. - void exportMeasuredData( - const llvm::Twine &OutputPath, - llvm::StringRef ProjectId = "default-phasar-project", - const std::vector *Modules = nullptr, - const std::vector *DataFlowAnalyses = nullptr); - - [[nodiscard]] const auto &getHistogram() const noexcept { return Histogram; } - private: llvm::StringMap RunningTimer; llvm::StringMap> StoppedTimer; diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index c99b88e9e5..b8115e4e47 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -18,6 +18,7 @@ #define PHASAR_UTILS_PAMMMACROS_H_ #include "phasar/Config/phasar-config.h" +#include "phasar/Utils/Macros.h" namespace psr { /// Defines the different level of severity of PAMM's performance evaluation @@ -52,6 +53,15 @@ inline constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = #HISTOGRAM_ID, &PAMMCategory> \ HISTOGRAM_ID +#define PAMM_TIMER(TIMER_ID, SEV_LVL) \ + static inline ::psr::pamm::Timer= \ + PAMM_SEVERITY_LEVEL::SEV_LVL, \ + #TIMER_ID, &PAMMCategory> \ + TIMER_ID + +#define PAMM_SCOPED_TIMER(TIMER_ID) \ + ::psr::pamm::ScopedTimer PSR_CONCAT(PAMMScopedTimer, __COUNTER__) { TIMER_ID } + #if defined(PAMM_FULL) || defined(PAMM_CORE) // Only include PAMM header if it is used #include "phasar/Utils/PAMM.h" diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp index 8f60e43450..5650ffea07 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp @@ -35,9 +35,10 @@ using namespace psr; namespace { PAMM_CATEGORY(IFDSConstAnalysis); -PAMM_COUNTER(Calls_getContextRelevantAliasSet, 0, Full); // NOLINT +PAMM_COUNTER(Calls_getContextRelevantAliasSet, 0, Full); // NOLINT PAMM_HISTOGRAM(ContextRelevantPointer, Full); +PAMM_TIMER(ContextRelevantAliasComputationTm, Full); } // namespace IFDSConstAnalysis::IFDSConstAnalysis(const LLVMProjectIRDB *IRDB, @@ -195,9 +196,8 @@ void IFDSConstAnalysis::printInitMemoryLocations() { std::set IFDSConstAnalysis::getContextRelevantAliasSet( std::set &AliasSet, IFDSConstAnalysis::f_t CurrentContext) { - PAMM_GET_INSTANCE; Calls_getContextRelevantAliasSet++; - START_TIMER("Context-Relevant-Alias-Set Computation", Full); + PAMM_SCOPED_TIMER(ContextRelevantAliasComputationTm); std::set ToGenerate; for (const auto *Alias : AliasSet) { PHASAR_LOG_LEVEL(DEBUG, "Alias: " << llvmIRToString(Alias)); @@ -225,13 +225,12 @@ std::set IFDSConstAnalysis::getContextRelevantAliasSet( } } // ignore everything else } - PAUSE_TIMER("Context-Relevant-Alias-Set Computation", Full); ContextRelevantPointer.add(ToGenerate.size(), 1); return ToGenerate; } bool IFDSConstAnalysis::isInitialized(IFDSConstAnalysis::d_t Fact) const { - return llvm::isa(Fact) || Initialized.count(Fact); + return llvm::isa(Fact) || Initialized.contains(Fact); } void IFDSConstAnalysis::markAsInitialized(IFDSConstAnalysis::d_t Fact) { diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index b3a3aee5b9..8de1ede11e 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -43,152 +43,8 @@ PAMM &PAMM::getInstance() { return Instance; } -void PAMM::startTimer(llvm::StringRef TimerId) { - if (LLVM_UNLIKELY(StoppedTimer.count(TimerId))) { - llvm::report_fatal_error("Do not start an already stopped timer"); - } - - auto [It, Inserted] = RunningTimer.try_emplace(TimerId); - if (LLVM_UNLIKELY(!Inserted)) { - llvm::report_fatal_error("Do not start an already running timer"); - } - - PAMM::TimePoint_t Start = std::chrono::steady_clock::now(); - It->second = Start; -} - -void PAMM::resetTimer(llvm::StringRef TimerId) { - [[maybe_unused]] bool InRunningTimers = RunningTimer.erase(TimerId); - [[maybe_unused]] bool InStoppedTimers = StoppedTimer.erase(TimerId); - - assert((InRunningTimers && !InStoppedTimers) || - (!InRunningTimers && InStoppedTimers) && - "resetTimer failed due to an invalid timer id"); -} - -void PAMM::stopTimer(llvm::StringRef TimerId, bool PauseTimer) { - auto RunningIt = RunningTimer.find(TimerId); - auto StoppedIt = StoppedTimer.find(TimerId); - bool TimerRunning = RunningIt != RunningTimer.end(); - bool TimerStopped = StoppedIt != StoppedTimer.end(); - bool ValidTimerId = TimerRunning || TimerStopped; - assert(ValidTimerId && "stopTimer failed due to an invalid timer id or timer " - "was already stopped"); - assert(TimerRunning && "stopTimer failed because timer was already stopped"); - - if (LLVM_LIKELY(ValidTimerId)) { - PAMM::TimePoint_t End = std::chrono::steady_clock::now(); - PAMM::TimePoint_t Start = RunningIt->second; - RunningTimer.erase(RunningIt); - auto P = make_pair(Start, End); - if (PauseTimer) { - RepeatingTimer[TimerId].push_back(P); - } else { - StoppedTimer[TimerId] = P; - } - } -} - -uint64_t PAMM::elapsedTime(llvm::StringRef TimerId) { - auto RunningIt = RunningTimer.find(TimerId); - - if (RunningIt != RunningTimer.end()) { - PAMM::TimePoint_t End = std::chrono::steady_clock::now(); - PAMM::TimePoint_t Start = RunningIt->second; - auto Duration = std::chrono::duration_cast(End - Start); - return Duration.count(); - } - if (auto StoppedIt = StoppedTimer.find(TimerId); - StoppedIt != StoppedTimer.end()) { - auto [Start, End] = StoppedIt->second; - auto Duration = std::chrono::duration_cast(End - Start); - return Duration.count(); - } - - assert(false && "elapsedTime failed due to an invalid timer id"); - return 0; -} - -template -static void foreachElapsedTimeOfRepeatingTimer( - llvm::StringMap< - std::vector>> - &RepeatingTimer, - HandlerFn Handler) { - for (const auto &Timer : RepeatingTimer) { - std::invoke( - Handler, Timer.first(), [&Timer](std::vector &AccTimeVec) { - AccTimeVec.reserve(Timer.second.size()); - - for (auto [Start, End] : Timer.second) { - auto Duration = - std::chrono::duration_cast(End - Start); - AccTimeVec.push_back(Duration.count()); - } - }); - } -} - -llvm::StringMap> PAMM::elapsedTimeOfRepeatingTimer() { - llvm::StringMap> AccTimes; - - foreachElapsedTimeOfRepeatingTimer( - RepeatingTimer, [&AccTimes](llvm::StringRef Id, auto Handler) { - std::invoke(std::move(Handler), AccTimes[Id]); - }); - - return AccTimes; -} - -std::string PAMM::getPrintableDuration(uint64_t Duration) { - return hms(Duration_t{Duration}).str(); -} - -void PAMM::stopAllTimers() { - while (!RunningTimer.empty()) { - // safe copy - auto Id = RunningTimer.begin()->first().str(); - stopTimer(Id); - } -} - void PAMM::printTimers(llvm::raw_ostream &OS) { - // stop all running timer - stopAllTimers(); - - OS << "Single Timer\n"; - OS << "------------\n"; - for (const auto &Timer : StoppedTimer) { - uint64_t Time = elapsedTime(Timer.first()); - OS << Timer.first() << " : " << getPrintableDuration(Time) << '\n'; - } - if (StoppedTimer.empty()) { - OS << "No single Timer started!\n\n"; - } else { - OS << "\n"; - } - OS << "Repeating Timer\n"; - OS << "---------------\n"; - - foreachElapsedTimeOfRepeatingTimer(RepeatingTimer, - [&OS](llvm::StringRef Id, auto Handler) { - OS << Id << " Timer:\n"; - std::vector Times; - std::invoke(std::move(Handler), Times); - - uint64_t Sum = 0; - for (auto Duration : Times) { - Sum += Duration; - OS << Duration << '\n'; - } - OS << "===\n" << Sum << "\n\n"; - }); - - if (RepeatingTimer.empty()) { - OS << "No repeating Timer found!\n"; - } else { - OS << '\n'; - } + pamm::Registry::instance().printTimers(OS); } static void printCountersImpl( @@ -286,113 +142,74 @@ void pamm::Registry::printHistograms(llvm::raw_ostream &OS, printHistogramsImpl(OS, Cat, *Hists); } -void PAMM::printMeasuredData(llvm::raw_ostream &Os) { - Os << "\n----- START OF EVALUATION DATA -----\n\n"; - printTimers(Os); - printCounters(Os); - printHistograms(Os); - Os << "\n----- END OF EVALUATION DATA -----\n\n"; -} - -void PAMM::exportMeasuredData( - const llvm::Twine &OutputPath, llvm::StringRef ProjectId, - const std::vector *Modules, - const std::vector *DataFlowAnalyses) { - // json file for holding all data - json JsonData; - - stopAllTimers(); - { - // add timer data - json JTimer; - for (const auto &Timer : StoppedTimer) { - uint64_t Time = elapsedTime(Timer.first()); - JTimer[Timer.first().str()] = Time; +static void printTimersImpl( + llvm::raw_ostream &OS, const pamm::Category &Cat, + const llvm::DenseMap &CatTms) { + OS << Cat.name() << ":\n"; + for (const auto &[Name, Tm] : CatTms) { + auto Time = Tm->Acc; + bool StillRunning = Tm->Tm.has_value(); + OS << " " << Name << ":\t"; + if (StillRunning) { + Time += Tm->Tm->elapsedNanos(); + OS << hms{Time} << " (still running)\n"; + } else { + OS << hms{Time} << '\n'; } - - foreachElapsedTimeOfRepeatingTimer( - RepeatingTimer, [&JTimer](llvm::StringRef Id, auto Handler) { - std::vector Times; - std::invoke(std::move(Handler), Times); - JTimer[Id.str()] = std::move(Times); - }); - - JsonData["Timer"] = std::move(JTimer); } + OS << '\n'; +} - { - // add histogram data if available - json JHistogram; - for (const auto &H : Histogram) { - json JSetH; - for (const auto &Entry : H.second) { - JSetH[Entry.first()] = Entry.second; - } - JHistogram[H.first()] = std::move(JSetH); - } - if (!JHistogram.is_null()) { - JsonData["Histogram"] = std::move(JHistogram); +void pamm::Registry::printTimers(llvm::raw_ostream &OS) const { + OS << "\nTimers\n"; + OS << "--------------\n"; + for (const auto &[Cat, CatTms] : Timers) { + if (Cat->isEnabled()) { + printTimersImpl(OS, *Cat, CatTms); } } - { - // add counter data - json JCounter; - for (const auto &Counter : Counter) { - JCounter[Counter.first()] = Counter.second; - } - JsonData["Counter"] = std::move(JCounter); + if (Histograms.empty()) { + OS << "No timers tracked!\n"; } - { - // add analysis/project/source file information if available - json JInfo; - JInfo["Project-ID"] = ProjectId; +} - if (Modules) { - JInfo["Module(s)"] = *Modules; - } - if (DataFlowAnalyses) { - JInfo["Data-flow analysis"] = *DataFlowAnalyses; - } - if (!JInfo.is_null()) { - JsonData["Info"] = std::move(JInfo); - } +void pamm::Registry::printTimers(llvm::raw_ostream &OS, + const Category &Cat) const { + if (!Cat.isEnabled()) { + OS << "Category '" << Cat.name() << "' is disabled\n"; + return; } - - llvm::SmallString<128> Buf; - OutputPath.toStringRef(Buf); - if (!llvm::StringRef(Buf).ends_with(".json")) { - Buf.append(".json"); + const auto *CatTms = getOrNull(Timers, &Cat); + if (!CatTms || CatTms->empty()) { + OS << "No Timers for category '" << Cat.name() << "' registered!\n"; + return; } - std::error_code EC; - llvm::raw_fd_ostream OS(Buf, EC); - - if (EC) { - throw std::system_error(EC); - } + OS << "\nTimers\n"; + OS << "--------------\n"; + printTimersImpl(OS, Cat, *CatTms); +} - OS << JsonData << '\n'; +void PAMM::printMeasuredData(llvm::raw_ostream &OS) { + OS << "\n----- START OF EVALUATION DATA -----\n\n"; + auto &Reg = pamm::Registry::instance(); + Reg.printTimers(OS); + Reg.printCounters(OS); + Reg.printHistograms(OS); + OS << "\n----- END OF EVALUATION DATA -----\n\n"; } void PAMM::printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { OS << "\n----- START OF EVALUATION DATA -----\n\n"; + scope_exit Pop = [&] { OS << "\n----- END OF EVALUATION DATA -----\n\n"; }; if (!Cat.isEnabled()) { OS << "Category '" << Cat.name() << "' is disabled\n"; return; } auto &Reg = pamm::Registry::instance(); - // Reg.printTimers(OS); + Reg.printTimers(OS, Cat); Reg.printCounters(OS, Cat); Reg.printHistograms(OS, Cat); - OS << "\n----- END OF EVALUATION DATA -----\n\n"; -} - -void PAMM::reset() { - RunningTimer.clear(); - StoppedTimer.clear(); - RepeatingTimer.clear(); - Counter.clear(); - Histogram.clear(); } auto pamm::Registry::findCategory(llvm::StringRef Name) const From 6e849fce9aff19959bd7a9d6df2ee3b9c52a04ca Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Wed, 24 Jun 2026 20:03:24 +0200 Subject: [PATCH 07/18] Fix qualifiers() + some minor stuff --- include/phasar/Utils/PAMM.h | 54 ++++++++++++++++++------------- include/phasar/Utils/PAMMMacros.h | 3 +- include/phasar/Utils/Utilities.h | 3 ++ lib/Utils/PAMM.cpp | 30 ++--------------- lib/Utils/Utilities.cpp | 23 ++++++------- 5 files changed, 50 insertions(+), 63 deletions(-) diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index ff091c395c..f6e066f3da 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -15,6 +15,7 @@ #include "phasar/Utils/TemplateString.h" #include "phasar/Utils/Timer.h" #include "phasar/Utils/TypeTraits.h" +#include "phasar/Utils/Utilities.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" @@ -32,7 +33,6 @@ #include // string #include #include -#include // vector namespace llvm { class raw_ostream; @@ -149,6 +149,7 @@ class Counter : private detail::CounterBase { [[nodiscard]] constexpr std::string qualifier() const { auto Ret = std::string(Cat->name()); + Ret += "::"; Ret += llvm::StringRef(Name); return Ret; } @@ -175,6 +176,7 @@ class Counter { [[nodiscard]] constexpr std::string qualifier() const { auto Ret = std::string(Cat->name()); + Ret += "::"; Ret += llvm::StringRef(Name); return Ret; } @@ -216,6 +218,7 @@ class Histogram : private detail::HistogramBase { [[nodiscard]] constexpr std::string qualifier() const { auto Ret = std::string(Cat->name()); + Ret += "::"; Ret += llvm::StringRef(Name); return Ret; } @@ -248,6 +251,7 @@ class Histogram { [[nodiscard]] constexpr std::string qualifier() const { auto Ret = std::string(Cat->name()); + Ret += "::"; Ret += llvm::StringRef(Name); return Ret; } @@ -282,6 +286,7 @@ class Timer : private detail::TimerBase { [[nodiscard]] constexpr std::string qualifier() const { auto Ret = std::string(Cat->name()); + Ret += "::"; Ret += llvm::StringRef(Name); return Ret; } @@ -309,6 +314,7 @@ class Timer { [[nodiscard]] constexpr std::string qualifier() const { auto Ret = std::string(Cat->name()); + Ret += "::"; Ret += llvm::StringRef(Name); return Ret; } @@ -363,28 +369,25 @@ class Registry { void printTimers(llvm::raw_ostream &OS) const; void printTimers(llvm::raw_ostream &OS, const Category &Cat) const; - /// Performs a linear search on all registered elements to find the category - /// with the given name. If none is found, returns nullptr. - /// - /// This method can be rather expensive. [[nodiscard]] const Category *findCategory(llvm::StringRef Name) const; private: - static void registerImpl(auto *Elem, auto &Into, const Category *Cat, - llvm::StringRef Name, llvm::StringRef ElemKind, - std::source_location Loc) { + void registerImpl(auto *Elem, auto &Into, const Category *Cat, + llvm::StringRef Name, llvm::StringRef ElemKind, + std::source_location Loc) { assert(Elem != nullptr); + assert(Cat != nullptr); + + RegisteredCategories.try_emplace(Cat->name(), Cat); + auto [It, Inserted] = Into[Cat].try_emplace(llvm::StringRef(Name), Elem->base()); if (!Inserted) [[unlikely]] { llvm::report_fatal_error( - "At " + llvm::Twine(Loc.file_name()) + ":" + llvm::Twine(Loc.line()) + - ":" + llvm::Twine(Loc.column()) + ": " + ElemKind + " " + + "At " + llvm::Twine(locToString(Loc)) + ": " + ElemKind + " " + llvm::Twine(Elem->qualifier()) + " already registered! Previous definition was here: " + - llvm::Twine(It->second->Loc.file_name()) + ":" + - llvm::Twine(It->second->Loc.line()) + ":" + - llvm::Twine(It->second->Loc.column())); + llvm::Twine(locToString(It->second->Loc))); } } @@ -420,6 +423,8 @@ class Registry { llvm::DenseMap> Timers; + + llvm::StringMap RegisteredCategories; }; template @@ -446,6 +451,15 @@ inline Timer::Timer(std::source_location Loc) noexcept { this->TheCategory = Cat; Registry::instance().registerTimer(this, Loc); } + +/// \brief Prints the measured data from all registered categories into the +/// given output stream +void printMeasuredData(llvm::raw_ostream &OS); + +/// \brief Prints the measured data from the given category into the given +/// output stream +void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat); + } // namespace pamm /// This class offers functionality to measure different performance metrics. @@ -497,16 +511,10 @@ class PAMM final { void printHistograms(llvm::raw_ostream &OS); /// \brief Prints the measured data to the commandline - void printMeasuredData(llvm::raw_ostream &OS); - void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat); - -private: - llvm::StringMap RunningTimer; - llvm::StringMap> StoppedTimer; - llvm::StringMap>> - RepeatingTimer; - llvm::StringMap Counter; - llvm::StringMap> Histogram; + void printMeasuredData(llvm::raw_ostream &OS) { pamm::printMeasuredData(OS); } + void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { + pamm::printMeasuredData(OS, Cat); + } }; } // namespace psr diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index b8115e4e47..bdb6cb4ada 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -19,6 +19,7 @@ #include "phasar/Config/phasar-config.h" #include "phasar/Utils/Macros.h" +#include "phasar/Utils/PAMM.h" namespace psr { /// Defines the different level of severity of PAMM's performance evaluation @@ -63,8 +64,6 @@ inline constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = ::psr::pamm::ScopedTimer PSR_CONCAT(PAMMScopedTimer, __COUNTER__) { TIMER_ID } #if defined(PAMM_FULL) || defined(PAMM_CORE) -// Only include PAMM header if it is used -#include "phasar/Utils/PAMM.h" #define PAMM_GET_INSTANCE PAMM &pamm = PAMM::getInstance() #define PAMM_RESET pamm.reset() diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index 4204a0af67..5a2c038a1a 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -335,6 +336,8 @@ template void assertAllNotNull([[maybe_unused]] const T &Range) { } } +[[nodiscard]] std::string locToString(std::source_location Loc); + } // namespace psr #endif diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 8de1ede11e..469b098841 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -18,25 +18,16 @@ #include "phasar/Utils/ChronoUtils.h" #include "phasar/Utils/MapUtils.h" -#include "phasar/Utils/NlohmannLogging.h" -#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" - #include #include -#include using namespace psr; -using json = nlohmann::json; - -namespace psr { PAMM &PAMM::getInstance() { static PAMM Instance{}; @@ -190,7 +181,7 @@ void pamm::Registry::printTimers(llvm::raw_ostream &OS, printTimersImpl(OS, Cat, *CatTms); } -void PAMM::printMeasuredData(llvm::raw_ostream &OS) { +void pamm::printMeasuredData(llvm::raw_ostream &OS) { OS << "\n----- START OF EVALUATION DATA -----\n\n"; auto &Reg = pamm::Registry::instance(); Reg.printTimers(OS); @@ -199,7 +190,7 @@ void PAMM::printMeasuredData(llvm::raw_ostream &OS) { OS << "\n----- END OF EVALUATION DATA -----\n\n"; } -void PAMM::printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { +void pamm::printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { OS << "\n----- START OF EVALUATION DATA -----\n\n"; scope_exit Pop = [&] { OS << "\n----- END OF EVALUATION DATA -----\n\n"; }; if (!Cat.isEnabled()) { @@ -214,20 +205,5 @@ void PAMM::printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { auto pamm::Registry::findCategory(llvm::StringRef Name) const -> const Category * { - for (const auto &[Cat, _] : Counters) { - if (Cat->name() == Name) { - return Cat; - } - } - - for (const auto &[Cat, _] : Histograms) { - if (Cat->name() == Name) { - return Cat; - } - } - - // TODO: Timers - - return nullptr; + return RegisteredCategories.lookup(Name); } -} // namespace psr diff --git a/lib/Utils/Utilities.cpp b/lib/Utils/Utilities.cpp index 9d8be8ab88..433580b98b 100644 --- a/lib/Utils/Utilities.cpp +++ b/lib/Utils/Utilities.cpp @@ -9,22 +9,16 @@ #include "phasar/Utils/Utilities.h" -#include "phasar/Utils/Logger.h" - -#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" -#include "llvm/IR/DerivedTypes.h" #include #include +#include -using namespace std; using namespace psr; -namespace psr { - -std::string createTimeStamp() { +std::string psr::createTimeStamp() { auto Now = std::chrono::system_clock::now(); auto NowTime = std::chrono::system_clock::to_time_t(Now); std::string TimeStr(std::ctime(&NowTime)); @@ -34,7 +28,7 @@ std::string createTimeStamp() { return TimeStr; } -bool isConstructor(llvm::StringRef MangledName) { +bool psr::isConstructor(llvm::StringRef MangledName) { // WARNING: Doesn't work for templated classes, should // the best way to do it I can think of is to use a lexer // on the name to detect the constructor point explained @@ -61,7 +55,7 @@ bool isConstructor(llvm::StringRef MangledName) { return false; } -bool isMangled(llvm::StringRef Name) { +bool psr::isMangled(llvm::StringRef Name) { // See llvm/Demangle/Demangle.cpp if (Name.starts_with("_Z") || Name.starts_with("___Z")) { // Itanium ABI @@ -98,4 +92,11 @@ bool StringIDLess::operator()(const std::string &Lhs, return LhsVal < RhsVal; } -} // namespace psr +std::string psr::locToString(std::source_location Loc) { + std::string Ret = Loc.file_name(); + Ret += ':'; + Ret += std::to_string(Loc.line()); + Ret += ':'; + Ret += std::to_string(Loc.column()); + return Ret; +} From 1fd418a9da58922af2eb2c347935e16d00abf5f5 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 27 Jun 2026 11:01:33 +0200 Subject: [PATCH 08/18] Make PAMM usable with unittests enabled + refactor PAMMTest --- include/phasar/Utils/PAMM.h | 144 +++++++---- include/phasar/Utils/PAMMMacros.h | 23 +- lib/Utils/PAMM.cpp | 44 +++- .../IfdsIde/InteractiveIDESolverTest.cpp | 4 +- unittests/Utils/PAMMTest.cpp | 237 +++++++++--------- 5 files changed, 246 insertions(+), 206 deletions(-) diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index f6e066f3da..9b971cff9b 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -40,20 +40,29 @@ class raw_ostream; namespace psr { +/// Defines the different level of severity of PAMM's performance evaluation +enum class PAMM_SEVERITY_LEVEL { Off = 0, Core, Full }; // NOLINT + +// NOLINTNEXTLINE +inline constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = +#if defined(PAMM_FULL) + PAMM_SEVERITY_LEVEL::Full; +#elif defined(PAMM_CORE) + PAMM_SEVERITY_LEVEL::Core; +#else + PAMM_SEVERITY_LEVEL::Off; +#endif + namespace pamm { class Registry; + +template class Category { public: explicit constexpr Category(llvm::StringLiteral Name, bool IsEnabled = true) noexcept - : Name(Name) -#if defined(PAMM_FULL) || defined(PAMM_CORE) - , - IsEnabled(IsEnabled) -#endif - { - } + : Name(Name), IsEnabled(IsEnabled) {} constexpr operator llvm::StringRef() const noexcept { return Name; } @@ -61,34 +70,42 @@ class Category { [[nodiscard]] constexpr auto isEnabled() const noexcept { return IsEnabled; } - constexpr void disable() noexcept { -#if defined(PAMM_FULL) || defined(PAMM_CORE) - IsEnabled = false; -#endif + constexpr void disable() noexcept { IsEnabled = false; } + constexpr void enable() noexcept { IsEnabled = true; } + +private: + llvm::StringRef Name; + bool IsEnabled = true; +}; + +template <> class Category { +public: + explicit constexpr Category(llvm::StringLiteral Name, + bool /*IsEnabled*/ = true) noexcept + : Name(Name) {} + + constexpr operator llvm::StringRef() const noexcept { return Name; } + + [[nodiscard]] constexpr llvm::StringRef name() const noexcept { return Name; } + + [[nodiscard]] constexpr auto isEnabled() const noexcept { + return std::false_type{}; } + constexpr void disable() noexcept {} + void enable() noexcept { -#if defined(PAMM_FULL) || defined(PAMM_CORE) - IsEnabled = true; -#else llvm::WithColor::warning() << "Cannot enable PAMM category '" << Name << "', because PAMM is disabled at compile time\n"; -#endif } private: llvm::StringRef Name; -#if defined(PAMM_FULL) || defined(PAMM_CORE) - bool IsEnabled = true; -#else - [[no_unique_address]] std::false_type IsEnabled{}; -#endif }; namespace detail { struct PAMMBase { - const Category *TheCategory{}; std::source_location Loc{}; }; struct CounterBase : PAMMBase { @@ -130,7 +147,7 @@ struct TimerBase : PAMMBase { class Registry; -template +template *Cat> class Counter : private detail::CounterBase { friend Registry; @@ -160,7 +177,7 @@ class Counter : private detail::CounterBase { [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } }; -template +template *Cat> class Counter { public: LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++() noexcept {} @@ -188,7 +205,7 @@ class Counter { namespace detail { struct IsCounterImpl { - template + template *Cat> static std::true_type test(Counter); static std::false_type test(...); @@ -198,7 +215,7 @@ template concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; } // namespace detail -template +template *Cat> class Histogram : private detail::HistogramBase { friend Registry; @@ -234,13 +251,18 @@ class Histogram : private detail::HistogramBase { add(psr::adl_to_string(PSR_FWD(DataPointId)), Increment); } + // For unit-tests + [[nodiscard]] const auto &internalRawData() const noexcept { + return HistData; + } + private: [[nodiscard]] constexpr detail::HistogramBase *base() noexcept { return this; } }; -template +template *Cat> class Histogram { friend Registry; @@ -265,7 +287,7 @@ class Histogram { uint64_t /*Increment*/) {} }; -template +template *Cat> class Timer : private detail::TimerBase { friend Registry; template friend class ScopedTimer; @@ -295,7 +317,7 @@ class Timer : private detail::TimerBase { [[nodiscard]] constexpr detail::TimerBase *base() noexcept { return this; } }; -template +template *Cat> class Timer { public: LLVM_ATTRIBUTE_ALWAYS_INLINE void start() noexcept {} @@ -322,14 +344,18 @@ class Timer { template class ScopedTimer { public: - template + template *Cat> constexpr ScopedTimer(Timer &Tm) : Tm(&Tm) { if (Cat->isEnabled()) { Tm.start(); } } - ~ScopedTimer() { Tm->stop(); } + ~ScopedTimer() { + if (Tm->Tm.has_value()) { + Tm->stop(); + } + } ScopedTimer(const ScopedTimer &) = delete; ScopedTimer &operator=(const ScopedTimer &) = delete; @@ -342,16 +368,16 @@ template class ScopedTimer { template <> class ScopedTimer { public: - template + template *Cat> constexpr ScopedTimer(Timer &Tm) {} }; class Registry { - template + template *Cat> friend class Counter; - template + template *Cat> friend class Histogram; - template + template *Cat> friend class Timer; public: @@ -361,18 +387,28 @@ class Registry { } void printCounters(llvm::raw_ostream &OS) const; - void printCounters(llvm::raw_ostream &OS, const Category &Cat) const; + void printCounters(llvm::raw_ostream &OS, const Category &Cat) const; + void printCounters(llvm::raw_ostream &OS, const Category &Cat) const {} void printHistograms(llvm::raw_ostream &OS) const; - void printHistograms(llvm::raw_ostream &OS, const Category &Cat) const; + void printHistograms(llvm::raw_ostream &OS, const Category &Cat) const; + void printHistograms(llvm::raw_ostream &OS, + const Category &Cat) const {} void printTimers(llvm::raw_ostream &OS) const; - void printTimers(llvm::raw_ostream &OS, const Category &Cat) const; + void printTimers(llvm::raw_ostream &OS, const Category &Cat) const; + void printTimers(llvm::raw_ostream &OS, const Category &Cat) const {} + + [[nodiscard]] const Category *findCategory(llvm::StringRef Name) const; + + // Sets all counters, histograms and timers back to 0. Useful for unit-tests + void reset() noexcept; - [[nodiscard]] const Category *findCategory(llvm::StringRef Name) const; + // Erases all registered PAMM-elements. Useful for unit-tests + void clear() noexcept; private: - void registerImpl(auto *Elem, auto &Into, const Category *Cat, + void registerImpl(auto *Elem, auto &Into, const Category *Cat, llvm::StringRef Name, llvm::StringRef ElemKind, std::source_location Loc) { assert(Elem != nullptr); @@ -391,64 +427,61 @@ class Registry { } } - template + template *Cat> void registerCounter( Counter *Ctr, std::source_location Loc = std::source_location::current()) noexcept { registerImpl(Ctr, Counters, Cat, Name, "Counter", Loc); } - template + template *Cat> void registerHistogram( Histogram *Hist, std::source_location Loc = std::source_location::current()) noexcept { registerImpl(Hist, Histograms, Cat, Name, "Histogram", Loc); } - template + template *Cat> void registerTimer( Timer *Tm, std::source_location Loc = std::source_location::current()) noexcept { registerImpl(Tm, Timers, Cat, Name, "Timer", Loc); } - llvm::DenseMap *, llvm::DenseMap> Counters; - llvm::DenseMap *, llvm::DenseMap> Histograms; - llvm::DenseMap *, llvm::DenseMap> Timers; - llvm::StringMap RegisteredCategories; + llvm::StringMap *> RegisteredCategories; }; -template +template *Cat> inline Counter::Counter(std::source_location Loc) noexcept { static_assert(Cat != nullptr); this->Loc = Loc; - this->TheCategory = Cat; Registry::instance().registerCounter(this, Loc); } -template +template *Cat> inline Histogram::Histogram( std::source_location Loc) noexcept { static_assert(Cat != nullptr); this->Loc = Loc; - this->TheCategory = Cat; Registry::instance().registerHistogram(this, Loc); } -template +template *Cat> inline Timer::Timer(std::source_location Loc) noexcept { static_assert(Cat != nullptr); this->Loc = Loc; - this->TheCategory = Cat; Registry::instance().registerTimer(this, Loc); } @@ -458,7 +491,9 @@ void printMeasuredData(llvm::raw_ostream &OS); /// \brief Prints the measured data from the given category into the given /// output stream -void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat); +void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat); +inline void printMeasuredData(llvm::raw_ostream &OS, + const pamm::Category &Cat) {} } // namespace pamm @@ -512,7 +547,8 @@ class PAMM final { /// \brief Prints the measured data to the commandline void printMeasuredData(llvm::raw_ostream &OS) { pamm::printMeasuredData(OS); } - void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { + void printMeasuredData(llvm::raw_ostream &OS, + const pamm::Category &Cat) { pamm::printMeasuredData(OS, Cat); } }; diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index bdb6cb4ada..023d2a462d 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -21,24 +21,8 @@ #include "phasar/Utils/Macros.h" #include "phasar/Utils/PAMM.h" -namespace psr { -/// Defines the different level of severity of PAMM's performance evaluation -enum class PAMM_SEVERITY_LEVEL { Off = 0, Core, Full }; // NOLINT - -// NOLINTNEXTLINE -inline constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = -#if defined(PAMM_FULL) - PAMM_SEVERITY_LEVEL::Full; -#elif defined(PAMM_CORE) - PAMM_SEVERITY_LEVEL::Core; -#else - PAMM_SEVERITY_LEVEL::Off; -#endif - -} // namespace psr - #define PAMM_CATEGORY(NAME, ...) \ - static inline ::psr::pamm::Category PAMMCategory { \ + static inline ::psr::pamm::Category<> PAMMCategory { \ #NAME __VA_OPT__(, ) __VA_ARGS__ \ } @@ -61,7 +45,10 @@ inline constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = TIMER_ID #define PAMM_SCOPED_TIMER(TIMER_ID) \ - ::psr::pamm::ScopedTimer PSR_CONCAT(PAMMScopedTimer, __COUNTER__) { TIMER_ID } + [[maybe_unused]] ::psr::pamm::ScopedTimer PSR_CONCAT(PAMMScopedTimer, \ + __COUNTER__) { \ + TIMER_ID \ + } #if defined(PAMM_FULL) || defined(PAMM_CORE) diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 469b098841..245fc4f8d4 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -39,7 +39,7 @@ void PAMM::printTimers(llvm::raw_ostream &OS) { } static void printCountersImpl( - llvm::raw_ostream &OS, const pamm::Category &Cat, + llvm::raw_ostream &OS, const pamm::Category &Cat, const llvm::DenseMap &CatCtrs) { OS << Cat.name() << ":\n"; @@ -64,7 +64,7 @@ void pamm::Registry::printCounters(llvm::raw_ostream &OS) const { } void pamm::Registry::printCounters(llvm::raw_ostream &OS, - const Category &Cat) const { + const Category &Cat) const { if (!Cat.isEnabled()) { OS << "Category '" << Cat.name() << "' is disabled\n"; return; @@ -88,8 +88,35 @@ void PAMM::printHistograms(llvm::raw_ostream &OS) { pamm::Registry::instance().printHistograms(OS); } +void pamm::Registry::reset() noexcept { + for (const auto &[Cat, Ctrs] : Counters) { + for (const auto &[_, Ctr] : Ctrs) { + Ctr->Ctr = 0; + } + } + + for (const auto &[Cat, Hists] : Histograms) { + for (const auto &[_, Hist] : Hists) { + Hist->HistData.clear(); + } + } + + for (const auto &[Cat, Tms] : Timers) { + for (const auto &[_, Tm] : Tms) { + Tm->reset(); + } + } +} + +void pamm::Registry::clear() noexcept { + Counters.clear(); + Histograms.clear(); + Timers.clear(); + RegisteredCategories.clear(); +} + static void printHistogramsImpl( - llvm::raw_ostream &OS, const pamm::Category &Cat, + llvm::raw_ostream &OS, const pamm::Category &Cat, const llvm::DenseMap &Hists) { for (const auto &[Name, H] : Hists) { @@ -117,7 +144,7 @@ void pamm::Registry::printHistograms(llvm::raw_ostream &OS) const { } void pamm::Registry::printHistograms(llvm::raw_ostream &OS, - const Category &Cat) const { + const Category &Cat) const { if (!Cat.isEnabled()) { OS << "Category '" << Cat.name() << "' is disabled\n"; return; @@ -134,7 +161,7 @@ void pamm::Registry::printHistograms(llvm::raw_ostream &OS, } static void printTimersImpl( - llvm::raw_ostream &OS, const pamm::Category &Cat, + llvm::raw_ostream &OS, const pamm::Category &Cat, const llvm::DenseMap &CatTms) { OS << Cat.name() << ":\n"; for (const auto &[Name, Tm] : CatTms) { @@ -165,7 +192,7 @@ void pamm::Registry::printTimers(llvm::raw_ostream &OS) const { } void pamm::Registry::printTimers(llvm::raw_ostream &OS, - const Category &Cat) const { + const Category &Cat) const { if (!Cat.isEnabled()) { OS << "Category '" << Cat.name() << "' is disabled\n"; return; @@ -190,7 +217,8 @@ void pamm::printMeasuredData(llvm::raw_ostream &OS) { OS << "\n----- END OF EVALUATION DATA -----\n\n"; } -void pamm::printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { +void pamm::printMeasuredData(llvm::raw_ostream &OS, + const pamm::Category &Cat) { OS << "\n----- START OF EVALUATION DATA -----\n\n"; scope_exit Pop = [&] { OS << "\n----- END OF EVALUATION DATA -----\n\n"; }; if (!Cat.isEnabled()) { @@ -204,6 +232,6 @@ void pamm::printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) { } auto pamm::Registry::findCategory(llvm::StringRef Name) const - -> const Category * { + -> const Category * { return RegisteredCategories.lookup(Name); } diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/InteractiveIDESolverTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/InteractiveIDESolverTest.cpp index 0848925e45..01f5c7a64e 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/InteractiveIDESolverTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/InteractiveIDESolverTest.cpp @@ -97,7 +97,9 @@ TEST_P(LinearConstant, ResultsEquivalentSolveUntilAsync) { EXPECT_EQ(std::nullopt, Result); if (!Result) { IsCancelled = false; - return std::move(Solver).solveWithAsyncCancellation(IsCancelled).value(); + return std::move(Solver) + .continueWithAsyncCancellation(IsCancelled) + .value(); } return Solver.consumeSolverResults(); }(); diff --git a/unittests/Utils/PAMMTest.cpp b/unittests/Utils/PAMMTest.cpp index 31e4adbdab..9935a07bd0 100644 --- a/unittests/Utils/PAMMTest.cpp +++ b/unittests/Utils/PAMMTest.cpp @@ -9,7 +9,11 @@ #include +namespace { using namespace psr; +using namespace std::chrono_literals; + +static constexpr pamm::Category TestingCat("PAMMTest"); /* Test fixture */ class PAMMTest : public ::testing::Test { @@ -20,150 +24,133 @@ class PAMMTest : public ::testing::Test { void SetUp() override {} - void TearDown() override { - PAMM &Pamm = PAMM::getInstance(); - Pamm.printMeasuredData(llvm::outs()); - Pamm.reset(); - } + void TearDown() override { pamm::Registry::instance().clear(); } }; TEST_F(PAMMTest, HandleTimer) { - // PAMM &pamm = PAMM::getInstance(); - PAMM::getInstance().startTimer("timer1"); - std::this_thread::sleep_for(std::chrono::milliseconds(120)); - PAMM::getInstance().stopTimer("timer1"); - auto Elapsed = PAMM::getInstance().elapsedTime("timer1"); - EXPECT_GE(Elapsed, 120U) << "Bad time measurement"; - EXPECT_LT(Elapsed, 240U) << "Too much tolerance"; + + pamm::Timer Tm1; + + { + pamm::ScopedTimer TmScope{Tm1}; + std::this_thread::sleep_for(std::chrono::milliseconds(120)); + } + auto Elapsed = Tm1.elapsedNanos(); + EXPECT_GE(Elapsed, 120ms) << "Bad time measurement"; + EXPECT_LT(Elapsed, 240ms) << "Too much tolerance"; + + pamm::printMeasuredData(llvm::outs()); } TEST_F(PAMMTest, HandleCounter) { - PAMM &Pamm = PAMM::getInstance(); - Pamm.regCounter("first"); - Pamm.regCounter("second"); - Pamm.regCounter("third"); - Pamm.incCounter("first", 42); - Pamm.incCounter("second"); - Pamm.incCounter("second"); - Pamm.incCounter("third", 2); - Pamm.decCounter("third", 2); - EXPECT_EQ(Pamm.getCounter("first"), 42); - EXPECT_EQ(Pamm.getCounter("second"), 2); - EXPECT_EQ(Pamm.getCounter("third"), 0); + pamm::Counter First; + pamm::Counter Second; + pamm::Counter Third; + + First += 42; + Second++; + ++Second; + + Third += 2; + Third -= 2; + + EXPECT_EQ(First.value(), 42); + EXPECT_EQ(Second.value(), 2); + EXPECT_EQ(Third.value(), 0); + + pamm::printMeasuredData(llvm::outs()); } -TEST_F(PAMMTest, HandleJSONOutput) { - PAMM &Pamm = PAMM::getInstance(); - Pamm.regCounter("timerCount"); - Pamm.regCounter("setOpCount"); - Pamm.startTimer("timer1"); - Pamm.incCounter("timerCount"); - Pamm.startTimer("timer2"); - Pamm.incCounter("timerCount"); - Pamm.regHistogram("Test-Set"); - Pamm.addToHistogram("Test-Set", "13"); - Pamm.addToHistogram("Test-Set", "13"); - Pamm.addToHistogram("Test-Set", "13"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "54"); - Pamm.addToHistogram("Test-Set", "54"); - Pamm.addToHistogram("Test-Set", "54"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.incCounter("setOpCount", 11); - - std::this_thread::sleep_for(std::chrono::milliseconds(180)); - Pamm.stopTimer("timer2"); - - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "42"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.incCounter("setOpCount", 10); - - Pamm.startTimer("timer3"); - Pamm.incCounter("timerCount"); - std::this_thread::sleep_for(std::chrono::milliseconds(230)); - - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "1"); - Pamm.addToHistogram("Test-Set", "2"); - Pamm.addToHistogram("Test-Set", "2"); - Pamm.addToHistogram("Test-Set", "2"); - Pamm.addToHistogram("Test-Set", "2"); - Pamm.incCounter("setOpCount", 9); - Pamm.stopTimer("timer3"); - Pamm.exportMeasuredData("HandleJSONOutputTest"); - - EXPECT_EQ(30, Pamm.getCounter("setOpCount")); - EXPECT_EQ(3, Pamm.getCounter("timerCount")); - - auto Timer1Elapsed = Pamm.elapsedTime("timer1"); - auto Timer2Elapsed = Pamm.elapsedTime("timer2"); - auto Timer3Elapsed = Pamm.elapsedTime("timer3"); - - EXPECT_GE(Timer1Elapsed, 410); // 180+230 - EXPECT_LT(Timer1Elapsed, 820); // 180+230 - - EXPECT_GE(Timer2Elapsed, 180); - EXPECT_LT(Timer2Elapsed, 360); - - EXPECT_GE(Timer3Elapsed, 230); - EXPECT_LT(Timer3Elapsed, 460); - - EXPECT_EQ(1, Pamm.getHistogram().size()); - EXPECT_EQ("Test-Set", Pamm.getHistogram().begin()->first()); +TEST_F(PAMMTest, HandleHistogram) { + pamm::Histogram TestSet; + TestSet.add(13, 1); + TestSet.add(13, 1); + TestSet.add(13, 1); + + TestSet.add(42, 1); + TestSet.add(42, 1); + TestSet.add(42, 1); + TestSet.add(42, 1); + TestSet.add(42, 1); + + TestSet.add(54, 1); + TestSet.add(54, 1); + TestSet.add(54, 1); + + TestSet.add(42, 1); + + TestSet.add(42, 7); + + TestSet.add(1, 4); + TestSet.add(1, 1); + TestSet.add(1, 1); + TestSet.add(1, 1); + TestSet.add(1, 1); + TestSet.add(1, 1); + TestSet.add(1, 1); + + TestSet.add(2, 4); llvm::StringMap Gt = { {"1", 10}, {"2", 4}, {"13", 3}, {"42", 13}, {"54", 3}, }; - const auto &Hist = Pamm.getHistogram().begin()->second; + const auto &Hist = TestSet.internalRawData(); EXPECT_EQ(Hist, Gt); -} -TEST_F(PAMMTest, HandleCounterRef) { - PAMM &Pamm = PAMM::getInstance(); - uint64_t &Ref1 = Pamm.getOrCreateCounterRef("cached"); - Ref1 += 5; - uint64_t &Ref2 = Pamm.getOrCreateCounterRef("cached"); - EXPECT_EQ(&Ref1, &Ref2) << "getOrCreateCounterRef must return a stable " - "reference to the same storage"; - Ref2 += 7; - EXPECT_EQ(Pamm.getCounter("cached"), 12U); + pamm::printMeasuredData(llvm::outs()); } -TEST_F(PAMMTest, HandleHistogramRef) { - PAMM &Pamm = PAMM::getInstance(); - llvm::StringMap &Hist1 = Pamm.getOrCreateHistogramRef("hist"); - Pamm.addToHistogram(Hist1, "a", 3); - Pamm.addToHistogram(Hist1, "b", 1); - Pamm.addToHistogram(Hist1, "a", 2); +// TEST_F(PAMMTest, HandleJSONOutput) { +// PAMM &Pamm = PAMM::getInstance(); +// Pamm.regCounter("timerCount"); +// Pamm.regCounter("setOpCount"); +// Pamm.startTimer("timer1"); +// Pamm.incCounter("timerCount"); +// Pamm.startTimer("timer2"); +// Pamm.incCounter("timerCount"); +// Pamm.regHistogram("Test-Set"); - llvm::StringMap &Hist2 = Pamm.getOrCreateHistogramRef("hist"); - EXPECT_EQ(&Hist1, &Hist2) << "getOrCreateHistogramRef must return a stable " - "reference to the same storage"; +// Pamm.incCounter("setOpCount", 11); - llvm::StringMap Gt = {{"a", 5}, {"b", 1}}; - EXPECT_EQ(Hist1, Gt); +// std::this_thread::sleep_for(std::chrono::milliseconds(180)); +// Pamm.stopTimer("timer2"); - // the StringRef-based overload must still operate on the same histogram - Pamm.addToHistogram("hist", "a"); - EXPECT_EQ(Hist1.lookup("a"), 6U); -} +// Pamm.incCounter("setOpCount", 10); + +// Pamm.startTimer("timer3"); +// Pamm.incCounter("timerCount"); +// std::this_thread::sleep_for(std::chrono::milliseconds(230)); + +// Pamm.incCounter("setOpCount", 9); +// Pamm.stopTimer("timer3"); +// Pamm.exportMeasuredData("HandleJSONOutputTest"); + +// EXPECT_EQ(30, Pamm.getCounter("setOpCount")); +// EXPECT_EQ(3, Pamm.getCounter("timerCount")); + +// auto Timer1Elapsed = Pamm.elapsedTime("timer1"); +// auto Timer2Elapsed = Pamm.elapsedTime("timer2"); +// auto Timer3Elapsed = Pamm.elapsedTime("timer3"); + +// EXPECT_GE(Timer1Elapsed, 410); // 180+230 +// EXPECT_LT(Timer1Elapsed, 820); // 180+230 + +// EXPECT_GE(Timer2Elapsed, 180); +// EXPECT_LT(Timer2Elapsed, 360); + +// EXPECT_GE(Timer3Elapsed, 230); +// EXPECT_LT(Timer3Elapsed, 460); + +// EXPECT_EQ(1, Pamm.getHistogram().size()); +// EXPECT_EQ("Test-Set", Pamm.getHistogram().begin()->first()); + +// llvm::StringMap Gt = { +// {"1", 10}, {"2", 4}, {"13", 3}, {"42", 13}, {"54", 3}, +// }; +// const auto &Hist = Pamm.getHistogram().begin()->second; +// EXPECT_EQ(Hist, Gt); +// } +} // namespace // main function for the test case int main(int Argc, char **Argv) { From f2a17cb7fb76ce2d2f801ea715943d9272cdd416 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 27 Jun 2026 11:41:41 +0200 Subject: [PATCH 09/18] Add MinMaxCounter + remove unused initial-value for counters --- .../IfdsIde/Solver/FlowEdgeFunctionCache.h | 40 ++++---- .../DataFlow/IfdsIde/Solver/IDESolver.h | 31 +++--- include/phasar/Utils/PAMM.h | 96 +++++++++++++++++++ include/phasar/Utils/PAMMMacros.h | 8 +- .../ControlFlow/LLVMBasedCallGraphBuilder.cpp | 6 +- .../IfdsIde/Problems/IFDSConstAnalysis.cpp | 2 +- lib/Utils/PAMM.cpp | 56 +++++++++++ 7 files changed, 197 insertions(+), 42 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index 85bc3cc0da..006080fb04 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -93,35 +93,35 @@ class FlowEdgeFunctionCache { PAMM_CATEGORY(FlowEdgeFunctionCache); // NOLINTBEGIN - PAMM_COUNTER(NormalFF_Construction, 0, Full); - PAMM_COUNTER(NormalFF_CacheHit, 0, Full); + PAMM_COUNTER(NormalFF_Construction, Full); + PAMM_COUNTER(NormalFF_CacheHit, Full); // Counters for the call flow functions - PAMM_COUNTER(CallFF_Construction, 0, Full); - PAMM_COUNTER(CallFF_CacheHit, 0, Full); + PAMM_COUNTER(CallFF_Construction, Full); + PAMM_COUNTER(CallFF_CacheHit, Full); // Counters for return flow functions - PAMM_COUNTER(ReturnFF_Construction, 0, Full); - PAMM_COUNTER(ReturnFF_CacheHit, 0, Full); + PAMM_COUNTER(ReturnFF_Construction, Full); + PAMM_COUNTER(ReturnFF_CacheHit, Full); // Counters for the call to return flow functions - PAMM_COUNTER(CallToRetFF_Construction, 0, Full); - PAMM_COUNTER(CallToRetFF_CacheHit, 0, Full); + PAMM_COUNTER(CallToRetFF_Construction, Full); + PAMM_COUNTER(CallToRetFF_CacheHit, Full); // Counters for the summary flow functions - PAMM_COUNTER(SummaryFF_Construction, 0, Full); - PAMM_COUNTER(SummaryFF_CacheHit, 0, Full); + PAMM_COUNTER(SummaryFF_Construction, Full); + PAMM_COUNTER(SummaryFF_CacheHit, Full); // Counters for the normal edge functions - PAMM_COUNTER(NormalEF_Construction, 0, Full); - PAMM_COUNTER(NormalEF_CacheHit, 0, Full); + PAMM_COUNTER(NormalEF_Construction, Full); + PAMM_COUNTER(NormalEF_CacheHit, Full); // Counters for the call edge functions - PAMM_COUNTER(CallEF_Construction, 0, Full); - PAMM_COUNTER(CallEF_CacheHit, 0, Full); + PAMM_COUNTER(CallEF_Construction, Full); + PAMM_COUNTER(CallEF_CacheHit, Full); // Counters for the return edge functions - PAMM_COUNTER(ReturnEF_Construction, 0, Full); - PAMM_COUNTER(ReturnEF_CacheHit, 0, Full); + PAMM_COUNTER(ReturnEF_Construction, Full); + PAMM_COUNTER(ReturnEF_CacheHit, Full); // Counters for the call to return edge functions - PAMM_COUNTER(CallToRetEF_Construction, 0, Full); - PAMM_COUNTER(CallToRetEF_CacheHit, 0, Full); + PAMM_COUNTER(CallToRetEF_Construction, Full); + PAMM_COUNTER(CallToRetEF_CacheHit, Full); // Counters for the summary edge functions - PAMM_COUNTER(SummaryEF_Construction, 0, Full); - PAMM_COUNTER(SummaryEF_CacheHit, 0, Full); + PAMM_COUNTER(SummaryEF_Construction, Full); + PAMM_COUNTER(SummaryEF_CacheHit, Full); // NOLINTEND diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 65705d7dab..ba82efd3d6 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -78,22 +78,21 @@ class IDESolver PAMM_CATEGORY(IDESolver); // NOLINTBEGIN - PAMM_COUNTER(Genfacts, 0, Core); - PAMM_COUNTER(Killfacts, 0, Core); - PAMM_COUNTER(Summaryreuse, 0, Core); - PAMM_COUNTER(IntraPathEdges, 0, Core); - PAMM_COUNTER(InterPathEdges, 0, Core); - PAMM_COUNTER(FFQueries, 0, Full); - PAMM_COUNTER(EFQueries, 0, Full); - PAMM_COUNTER(ValuePropagation, 0, Full); - PAMM_COUNTER(ValueComputation, 0, Full); - PAMM_COUNTER(SpecialSummaryFF_Application, 0, Full); - PAMM_COUNTER(SpecialSummaryEF_Queries, 0, Full); - PAMM_COUNTER(JumpFnConstruction, 0, Full); - PAMM_COUNTER(ProcessCall, 0, Full); - PAMM_COUNTER(ProcessNormal, 0, Full); - PAMM_COUNTER(ProcessExit, 0, Full); - PAMM_COUNTER(Calls_getAliasSet, 0, Full); + PAMM_COUNTER(Genfacts, Core); + PAMM_COUNTER(Killfacts, Core); + PAMM_COUNTER(Summaryreuse, Core); + PAMM_COUNTER(IntraPathEdges, Core); + PAMM_COUNTER(InterPathEdges, Core); + PAMM_COUNTER(FFQueries, Full); + PAMM_COUNTER(EFQueries, Full); + PAMM_COUNTER(ValuePropagation, Full); + PAMM_COUNTER(ValueComputation, Full); + PAMM_COUNTER(SpecialSummaryFF_Application, Full); + PAMM_COUNTER(SpecialSummaryEF_Queries, Full); + PAMM_COUNTER(JumpFnConstruction, Full); + PAMM_COUNTER(ProcessCall, Full); + PAMM_COUNTER(ProcessNormal, Full); + PAMM_COUNTER(ProcessExit, Full); PAMM_HISTOGRAM(DataFlowFacts, Full); PAMM_HISTOGRAM(PointsTo, Full); diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 9b971cff9b..00c889f49f 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -10,6 +10,7 @@ *****************************************************************************/ #include "phasar/Config/phasar-config.h" +#include "phasar/Utils/Average.h" #include "phasar/Utils/ChronoUtils.h" #include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/TemplateString.h" @@ -23,11 +24,13 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" #include "llvm/Support/WithColor.h" #include // high_resolution_clock::time_point, milliseconds #include #include +#include #include #include #include // string @@ -112,6 +115,12 @@ struct CounterBase : PAMMBase { ptrdiff_t Ctr{}; // TODO: Add thread-safe counter }; +struct MinMaxCounterBase : PAMMBase { + size_t Min = SIZE_MAX; + size_t Max = 0; + Sampler Avg{}; +}; + struct HistogramBase : PAMMBase { llvm::StringMap HistData{}; }; @@ -215,6 +224,68 @@ template concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; } // namespace detail +template *Cat> +class MinMaxCounter : private detail::MinMaxCounterBase { + friend Registry; + +public: + inline explicit MinMaxCounter( + std::source_location Loc = std::source_location::current()) noexcept; + + constexpr void add(size_t Offset) noexcept { + Avg.addSample(Offset); + if (Offset > Max) { + Max = Offset; + } + if (Offset < Min) { + Min = Offset; + } + }; + + constexpr void operator++() noexcept { add(1); } + constexpr void operator++(int) noexcept { add(1); } + constexpr void operator+=(size_t Offset) noexcept { add(Offset); } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MinMaxCounter C) { + return OS << Cat->name() << "::" << Name << ": min(" << C.Min << "), max(" + << C.Max << "), avg: " << llvm::format("%g", C.Avg.getAverage()) + << ", #samples(" << C.Avg.getNumSamples() << ')'; + } + + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(Cat->name()); + Ret += "::"; + Ret += llvm::StringRef(Name); + return Ret; + } + +private: + [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } +}; + +template *Cat> +class MinMaxCounter { +public: + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void add(size_t Offset) noexcept {} + + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++() noexcept {} + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++(int) noexcept {} + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void + operator+=(size_t Offset) noexcept {} + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + MinMaxCounter /*C*/) { + return OS << Cat->name() << "::" << Name; + } + + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(Cat->name()); + Ret += "::"; + Ret += llvm::StringRef(Name); + return Ret; + } +}; + template *Cat> class Histogram : private detail::HistogramBase { friend Registry; @@ -390,6 +461,12 @@ class Registry { void printCounters(llvm::raw_ostream &OS, const Category &Cat) const; void printCounters(llvm::raw_ostream &OS, const Category &Cat) const {} + void printMinMaxCounters(llvm::raw_ostream &OS) const; + void printMinMaxCounters(llvm::raw_ostream &OS, + const Category &Cat) const; + void printMinMaxCounters(llvm::raw_ostream &OS, + const Category &Cat) const {} + void printHistograms(llvm::raw_ostream &OS) const; void printHistograms(llvm::raw_ostream &OS, const Category &Cat) const; void printHistograms(llvm::raw_ostream &OS, @@ -434,6 +511,13 @@ class Registry { registerImpl(Ctr, Counters, Cat, Name, "Counter", Loc); } + template *Cat> + void registerMMCounter( + MinMaxCounter *Ctr, + std::source_location Loc = std::source_location::current()) noexcept { + registerImpl(Ctr, MMCounters, Cat, Name, "MinMaxCounter", Loc); + } + template *Cat> void registerHistogram( Histogram *Hist, @@ -452,6 +536,10 @@ class Registry { llvm::DenseMap> Counters; + llvm::DenseMap *, + llvm::DenseMap> + MMCounters; + llvm::DenseMap *, llvm::DenseMap> Histograms; @@ -470,6 +558,14 @@ inline Counter::Counter(std::source_location Loc) noexcept { Registry::instance().registerCounter(this, Loc); } +template *Cat> +inline MinMaxCounter::MinMaxCounter( + std::source_location Loc) noexcept { + static_assert(Cat != nullptr); + this->Loc = Loc; + Registry::instance().registerMMCounter(this, Loc); +} + template *Cat> inline Histogram::Histogram( std::source_location Loc) noexcept { diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index 023d2a462d..e4fa7c1ee8 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -26,12 +26,18 @@ #NAME __VA_OPT__(, ) __VA_ARGS__ \ } -#define PAMM_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL) \ +#define PAMM_COUNTER(COUNTER_ID, SEV_LVL) \ static inline ::psr::pamm::Counter= \ PAMM_SEVERITY_LEVEL::SEV_LVL, \ #COUNTER_ID, &PAMMCategory> \ COUNTER_ID +#define PAMM_MINMAX_COUNTER(COUNTER_ID, SEV_LVL) \ + static inline ::psr::pamm::MinMaxCounter= \ + PAMM_SEVERITY_LEVEL::SEV_LVL, \ + #COUNTER_ID, &PAMMCategory> \ + COUNTER_ID + #define PAMM_HISTOGRAM(HISTOGRAM_ID, SEV_LVL) \ static inline ::psr::pamm::Histogram= \ PAMM_SEVERITY_LEVEL::SEV_LVL, \ diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp index 838222c2a1..16add0927d 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp @@ -25,10 +25,8 @@ using namespace psr; PAMM_CATEGORY(CallGraphBuilder); -PAMM_COUNTER(CGFunctions, CGBuilder.viewCallGraph().getNumVertexFunctions(), - Full); -PAMM_COUNTER(CGCallSites, CGBuilder.viewCallGraph().getNumVertexCallSites(), - Full); +PAMM_COUNTER(CGFunctions, Full); +PAMM_COUNTER(CGCallSites, Full); struct Builder { const LLVMProjectIRDB *IRDB = nullptr; diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp index 5650ffea07..bb1e219d00 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.cpp @@ -36,7 +36,7 @@ using namespace psr; namespace { PAMM_CATEGORY(IFDSConstAnalysis); -PAMM_COUNTER(Calls_getContextRelevantAliasSet, 0, Full); // NOLINT +PAMM_COUNTER(Calls_getContextRelevantAliasSet, Full); // NOLINT PAMM_HISTOGRAM(ContextRelevantPointer, Full); PAMM_TIMER(ContextRelevantAliasComputationTm, Full); } // namespace diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 245fc4f8d4..6fa43ecab0 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -26,6 +26,7 @@ #include #include +#include using namespace psr; @@ -80,6 +81,50 @@ void pamm::Registry::printCounters(llvm::raw_ostream &OS, printCountersImpl(OS, Cat, *CatCtrs); } +static void printMMCountersImpl( + llvm::raw_ostream &OS, const pamm::Category &Cat, + const llvm::DenseMap + &CatCtrs) { + OS << Cat.name() << ":\n"; + for (const auto &[Name, C] : CatCtrs) { + OS << " " << Name << ": min(" << C->Min << "), max(" << C->Max + << "), avg: " << llvm::format("%g", C->Avg.getAverage()) << ", #samples(" + << C->Avg.getNumSamples() << ")\n"; + } + OS << '\n'; +} + +void pamm::Registry::printMinMaxCounters(llvm::raw_ostream &OS) const { + OS << "\nMin-Max-Counters\n"; + OS << "--------\n"; + + for (const auto &[Cat, CatCtrs] : MMCounters) { + if (Cat->isEnabled()) { + printMMCountersImpl(OS, *Cat, CatCtrs); + } + } + if (Counters.empty()) { + OS << "No MinMax-Counter registered!\n"; + } +} +void pamm::Registry::printMinMaxCounters(llvm::raw_ostream &OS, + const Category &Cat) const { + if (!Cat.isEnabled()) { + OS << "Category '" << Cat.name() << "' is disabled\n"; + return; + } + const auto *CatCtrs = getOrNull(MMCounters, &Cat); + if (!CatCtrs || CatCtrs->empty()) { + OS << "No Min-Max-Counters for category '" << Cat.name() + << "' registered!\n"; + return; + } + + OS << "\nMin-Max-Counters\n"; + OS << "--------\n"; + printMMCountersImpl(OS, Cat, *CatCtrs); +} + void PAMM::printCounters(llvm::raw_ostream &OS) { pamm::Registry::instance().printCounters(OS); } @@ -95,6 +140,14 @@ void pamm::Registry::reset() noexcept { } } + for (const auto &[Cat, Ctrs] : MMCounters) { + for (const auto &[_, Ctr] : Ctrs) { + Ctr->Min = SIZE_MAX; + Ctr->Max = 0; + Ctr->Avg = {}; + } + } + for (const auto &[Cat, Hists] : Histograms) { for (const auto &[_, Hist] : Hists) { Hist->HistData.clear(); @@ -110,6 +163,7 @@ void pamm::Registry::reset() noexcept { void pamm::Registry::clear() noexcept { Counters.clear(); + MMCounters.clear(); Histograms.clear(); Timers.clear(); RegisteredCategories.clear(); @@ -213,6 +267,7 @@ void pamm::printMeasuredData(llvm::raw_ostream &OS) { auto &Reg = pamm::Registry::instance(); Reg.printTimers(OS); Reg.printCounters(OS); + Reg.printMinMaxCounters(OS); Reg.printHistograms(OS); OS << "\n----- END OF EVALUATION DATA -----\n\n"; } @@ -228,6 +283,7 @@ void pamm::printMeasuredData(llvm::raw_ostream &OS, auto &Reg = pamm::Registry::instance(); Reg.printTimers(OS, Cat); Reg.printCounters(OS, Cat); + Reg.printMinMaxCounters(OS, Cat); Reg.printHistograms(OS, Cat); } From 2b6baf1850e8b6dea01f2b379fbb12b43873a526 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 27 Jun 2026 12:48:49 +0200 Subject: [PATCH 10/18] Let AI reduce some boilerplate --- include/phasar/Utils/PAMM.h | 102 ++++++----------- lib/Utils/PAMM.cpp | 223 +++++++++++++++--------------------- 2 files changed, 128 insertions(+), 197 deletions(-) diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 00c889f49f..1bf5f1b798 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -152,15 +152,27 @@ struct TimerBase : PAMMBase { [[nodiscard]] hms elapsed() const noexcept { return Acc; } }; + +template struct Qualified { + [[nodiscard]] constexpr std::string qualifier() const { + auto Ret = std::string(Cat->name()); + Ret += "::"; + Ret += llvm::StringRef(Name); + return Ret; + } +}; } // namespace detail class Registry; template *Cat> -class Counter : private detail::CounterBase { +class Counter : private detail::CounterBase, + private detail::Qualified { friend Registry; public: + using detail::Qualified::qualifier; + inline explicit Counter( std::source_location Loc = std::source_location::current()) noexcept; @@ -173,13 +185,6 @@ class Counter : private detail::CounterBase { return OS << Cat->name() << "::" << Name << ": " << C.Ctr; } - [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(Cat->name()); - Ret += "::"; - Ret += llvm::StringRef(Name); - return Ret; - } - [[nodiscard]] constexpr ptrdiff_t value() const noexcept { return Ctr; } private: @@ -187,8 +192,10 @@ class Counter : private detail::CounterBase { }; template *Cat> -class Counter { +class Counter : private detail::Qualified { public: + using detail::Qualified::qualifier; + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++() noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++(int) noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void @@ -200,13 +207,6 @@ class Counter { return OS << Cat->name() << "::" << Name; } - [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(Cat->name()); - Ret += "::"; - Ret += llvm::StringRef(Name); - return Ret; - } - [[nodiscard]] constexpr std::nullopt_t value() const noexcept { return std::nullopt; } @@ -225,10 +225,13 @@ concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; } // namespace detail template *Cat> -class MinMaxCounter : private detail::MinMaxCounterBase { +class MinMaxCounter : private detail::MinMaxCounterBase, + private detail::Qualified { friend Registry; public: + using detail::Qualified::qualifier; + inline explicit MinMaxCounter( std::source_location Loc = std::source_location::current()) noexcept; @@ -252,20 +255,15 @@ class MinMaxCounter : private detail::MinMaxCounterBase { << ", #samples(" << C.Avg.getNumSamples() << ')'; } - [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(Cat->name()); - Ret += "::"; - Ret += llvm::StringRef(Name); - return Ret; - } - private: [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } }; template *Cat> -class MinMaxCounter { +class MinMaxCounter : private detail::Qualified { public: + using detail::Qualified::qualifier; + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void add(size_t Offset) noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++() noexcept {} @@ -277,20 +275,16 @@ class MinMaxCounter { MinMaxCounter /*C*/) { return OS << Cat->name() << "::" << Name; } - - [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(Cat->name()); - Ret += "::"; - Ret += llvm::StringRef(Name); - return Ret; - } }; template *Cat> -class Histogram : private detail::HistogramBase { +class Histogram : private detail::HistogramBase, + private detail::Qualified { friend Registry; public: + using detail::Qualified::qualifier; + inline explicit Histogram( std::source_location Loc = std::source_location::current()) noexcept; @@ -304,13 +298,6 @@ class Histogram : private detail::HistogramBase { return OS; } - [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(Cat->name()); - Ret += "::"; - Ret += llvm::StringRef(Name); - return Ret; - } - void add(llvm::StringRef DataPointId, uint64_t Increment) { if (Cat->isEnabled()) { this->HistData[DataPointId] += Increment; @@ -334,21 +321,16 @@ class Histogram : private detail::HistogramBase { }; template *Cat> -class Histogram { +class Histogram : private detail::Qualified { friend Registry; public: + using detail::Qualified::qualifier; + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Histogram /*H*/) { return OS << Cat->name() << "::" << Name << "\n"; } - [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(Cat->name()); - Ret += "::"; - Ret += llvm::StringRef(Name); - return Ret; - } - LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void add(llvm::StringRef /*DataPointId*/, uint64_t /*Increment*/) {} @@ -359,11 +341,13 @@ class Histogram { }; template *Cat> -class Timer : private detail::TimerBase { +class Timer : private detail::TimerBase, private detail::Qualified { friend Registry; template friend class ScopedTimer; public: + using detail::Qualified::qualifier; + inline explicit Timer( std::source_location Loc = std::source_location::current()) noexcept; @@ -377,20 +361,15 @@ class Timer : private detail::TimerBase { return OS << Cat->name() << "::" << Name << ": " << T.elapsed(); } - [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(Cat->name()); - Ret += "::"; - Ret += llvm::StringRef(Name); - return Ret; - } - private: [[nodiscard]] constexpr detail::TimerBase *base() noexcept { return this; } }; template *Cat> -class Timer { +class Timer : private detail::Qualified { public: + using detail::Qualified::qualifier; + LLVM_ATTRIBUTE_ALWAYS_INLINE void start() noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE void stop() noexcept {} @@ -404,13 +383,6 @@ class Timer { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Timer /*T*/) { return OS << Cat->name() << "::" << Name; } - - [[nodiscard]] constexpr std::string qualifier() const { - auto Ret = std::string(Cat->name()); - Ret += "::"; - Ret += llvm::StringRef(Name); - return Ret; - } }; template class ScopedTimer { @@ -447,6 +419,8 @@ class Registry { template *Cat> friend class Counter; template *Cat> + friend class MinMaxCounter; + template *Cat> friend class Histogram; template *Cat> friend class Timer; diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 6fa43ecab0..9b4ccacbd3 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -17,6 +17,7 @@ #include "phasar/Utils/PAMM.h" #include "phasar/Utils/ChronoUtils.h" +#include "phasar/Utils/Fn.h" #include "phasar/Utils/MapUtils.h" #include "llvm/ADT/StringRef.h" @@ -35,8 +36,38 @@ PAMM &PAMM::getInstance() { return Instance; } -void PAMM::printTimers(llvm::raw_ostream &OS) { - pamm::Registry::instance().printTimers(OS); +template +static void printAllHelper(llvm::raw_ostream &OS, const MapTy &Map, + llvm::StringRef Header, llvm::StringRef Separator, + llvm::StringRef EmptyMsg, ImplFn Impl) { + OS << '\n' << Header << '\n' << Separator << '\n'; + for (const auto &[Cat, Items] : Map) { + if (Cat->isEnabled()) { + Impl(OS, *Cat, Items); + } + } + if (Map.empty()) { + OS << EmptyMsg << '\n'; + } +} + +template +static void printCategoryHelper(llvm::raw_ostream &OS, + const pamm::Category &Cat, + const MapTy &Map, llvm::StringRef TypeName, + llvm::StringRef Separator, ImplFn Impl) { + if (!Cat.isEnabled()) { + OS << "Category '" << Cat.name() << "' is disabled\n"; + return; + } + const auto *Items = getOrNull(Map, &Cat); + if (!Items || Items->empty()) { + OS << "No " << TypeName << " for category '" << Cat.name() + << "' registered!\n"; + return; + } + OS << '\n' << TypeName << '\n' << Separator << '\n'; + Impl(OS, Cat, *Items); } static void printCountersImpl( @@ -51,34 +82,14 @@ static void printCountersImpl( } void pamm::Registry::printCounters(llvm::raw_ostream &OS) const { - OS << "\nCounters\n"; - OS << "--------\n"; - - for (const auto &[Cat, CatCtrs] : Counters) { - if (Cat->isEnabled()) { - printCountersImpl(OS, *Cat, CatCtrs); - } - } - if (Counters.empty()) { - OS << "No Counter registered!\n"; - } + printAllHelper(OS, Counters, "Counters", "--------", "No Counter registered!", + printCountersImpl); } void pamm::Registry::printCounters(llvm::raw_ostream &OS, const Category &Cat) const { - if (!Cat.isEnabled()) { - OS << "Category '" << Cat.name() << "' is disabled\n"; - return; - } - const auto *CatCtrs = getOrNull(Counters, &Cat); - if (!CatCtrs || CatCtrs->empty()) { - OS << "No Counters for category '" << Cat.name() << "' registered!\n"; - return; - } - - OS << "\nCounters\n"; - OS << "--------\n"; - printCountersImpl(OS, Cat, *CatCtrs); + printCategoryHelper(OS, Cat, Counters, "Counters", "--------", + printCountersImpl); } static void printMMCountersImpl( @@ -95,34 +106,14 @@ static void printMMCountersImpl( } void pamm::Registry::printMinMaxCounters(llvm::raw_ostream &OS) const { - OS << "\nMin-Max-Counters\n"; - OS << "--------\n"; - - for (const auto &[Cat, CatCtrs] : MMCounters) { - if (Cat->isEnabled()) { - printMMCountersImpl(OS, *Cat, CatCtrs); - } - } - if (Counters.empty()) { - OS << "No MinMax-Counter registered!\n"; - } + printAllHelper(OS, MMCounters, "Min-Max-Counters", "--------", + "No MinMax-Counter registered!", printMMCountersImpl); } + void pamm::Registry::printMinMaxCounters(llvm::raw_ostream &OS, const Category &Cat) const { - if (!Cat.isEnabled()) { - OS << "Category '" << Cat.name() << "' is disabled\n"; - return; - } - const auto *CatCtrs = getOrNull(MMCounters, &Cat); - if (!CatCtrs || CatCtrs->empty()) { - OS << "No Min-Max-Counters for category '" << Cat.name() - << "' registered!\n"; - return; - } - - OS << "\nMin-Max-Counters\n"; - OS << "--------\n"; - printMMCountersImpl(OS, Cat, *CatCtrs); + printCategoryHelper(OS, Cat, MMCounters, "Min-Max-Counters", "--------", + printMMCountersImpl); } void PAMM::printCounters(llvm::raw_ostream &OS) { @@ -133,42 +124,6 @@ void PAMM::printHistograms(llvm::raw_ostream &OS) { pamm::Registry::instance().printHistograms(OS); } -void pamm::Registry::reset() noexcept { - for (const auto &[Cat, Ctrs] : Counters) { - for (const auto &[_, Ctr] : Ctrs) { - Ctr->Ctr = 0; - } - } - - for (const auto &[Cat, Ctrs] : MMCounters) { - for (const auto &[_, Ctr] : Ctrs) { - Ctr->Min = SIZE_MAX; - Ctr->Max = 0; - Ctr->Avg = {}; - } - } - - for (const auto &[Cat, Hists] : Histograms) { - for (const auto &[_, Hist] : Hists) { - Hist->HistData.clear(); - } - } - - for (const auto &[Cat, Tms] : Timers) { - for (const auto &[_, Tm] : Tms) { - Tm->reset(); - } - } -} - -void pamm::Registry::clear() noexcept { - Counters.clear(); - MMCounters.clear(); - Histograms.clear(); - Timers.clear(); - RegisteredCategories.clear(); -} - static void printHistogramsImpl( llvm::raw_ostream &OS, const pamm::Category &Cat, const llvm::DenseMap @@ -185,33 +140,14 @@ static void printHistogramsImpl( } void pamm::Registry::printHistograms(llvm::raw_ostream &OS) const { - OS << "\nHistograms\n"; - OS << "--------------\n"; - for (const auto &[Cat, Hists] : Histograms) { - if (Cat->isEnabled()) { - printHistogramsImpl(OS, *Cat, Hists); - } - } - if (Histograms.empty()) { - OS << "No histograms tracked!\n"; - } + printAllHelper(OS, Histograms, "Histograms", "--------------", + "No histograms tracked!", fn); } void pamm::Registry::printHistograms(llvm::raw_ostream &OS, const Category &Cat) const { - if (!Cat.isEnabled()) { - OS << "Category '" << Cat.name() << "' is disabled\n"; - return; - } - const auto *Hists = getOrNull(Histograms, &Cat); - if (!Hists || Hists->empty()) { - OS << "No Histograms for category '" << Cat.name() << "' registered!\n"; - return; - } - - OS << "\nHistograms\n"; - OS << "--------------\n"; - printHistogramsImpl(OS, Cat, *Hists); + printCategoryHelper(OS, Cat, Histograms, "Histograms", "--------------", + fn); } static void printTimersImpl( @@ -233,33 +169,18 @@ static void printTimersImpl( } void pamm::Registry::printTimers(llvm::raw_ostream &OS) const { - OS << "\nTimers\n"; - OS << "--------------\n"; - for (const auto &[Cat, CatTms] : Timers) { - if (Cat->isEnabled()) { - printTimersImpl(OS, *Cat, CatTms); - } - } - if (Histograms.empty()) { - OS << "No timers tracked!\n"; - } + printAllHelper(OS, Timers, "Timers", "--------------", "No timers tracked!", + fn); +} + +void PAMM::printTimers(llvm::raw_ostream &OS) { + pamm::Registry::instance().printTimers(OS); } void pamm::Registry::printTimers(llvm::raw_ostream &OS, const Category &Cat) const { - if (!Cat.isEnabled()) { - OS << "Category '" << Cat.name() << "' is disabled\n"; - return; - } - const auto *CatTms = getOrNull(Timers, &Cat); - if (!CatTms || CatTms->empty()) { - OS << "No Timers for category '" << Cat.name() << "' registered!\n"; - return; - } - - OS << "\nTimers\n"; - OS << "--------------\n"; - printTimersImpl(OS, Cat, *CatTms); + printCategoryHelper(OS, Cat, Timers, "Timers", "--------------", + fn); } void pamm::printMeasuredData(llvm::raw_ostream &OS) { @@ -287,6 +208,42 @@ void pamm::printMeasuredData(llvm::raw_ostream &OS, Reg.printHistograms(OS, Cat); } +void pamm::Registry::reset() noexcept { + for (const auto &[Cat, Ctrs] : Counters) { + for (const auto &[_, Ctr] : Ctrs) { + Ctr->Ctr = 0; + } + } + + for (const auto &[Cat, Ctrs] : MMCounters) { + for (const auto &[_, Ctr] : Ctrs) { + Ctr->Min = SIZE_MAX; + Ctr->Max = 0; + Ctr->Avg = {}; + } + } + + for (const auto &[Cat, Hists] : Histograms) { + for (const auto &[_, Hist] : Hists) { + Hist->HistData.clear(); + } + } + + for (const auto &[Cat, Tms] : Timers) { + for (const auto &[_, Tm] : Tms) { + Tm->reset(); + } + } +} + +void pamm::Registry::clear() noexcept { + Counters.clear(); + MMCounters.clear(); + Histograms.clear(); + Timers.clear(); + RegisteredCategories.clear(); +} + auto pamm::Registry::findCategory(llvm::StringRef Name) const -> const Category * { return RegisteredCategories.lookup(Name); From 0df17ce091eb8ccb5fc23735c20ff3a60ad6041c Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 27 Jun 2026 13:03:28 +0200 Subject: [PATCH 11/18] Prevent different PAMM-element instances per IDESolver/FECache tmpl instantiation to improve compile-times --- .../IfdsIde/Solver/FlowEdgeFunctionCache.h | 46 ++++++++++--------- .../DataFlow/IfdsIde/Solver/IDESolver.h | 26 ++++++----- include/phasar/Utils/PAMM.h | 2 + 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index 006080fb04..c1f00cb93e 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -69,27 +69,8 @@ class LLVMMapKeyCompressor { llvm::DenseMap Map{}; }; -/** - * This class caches flow and edge functions to avoid their reconstruction. - * When a flow or edge function must be applied to multiple times, a cached - * version is used if existend, otherwise a new one is created and inserted - * into the cache. - */ -template > -class FlowEdgeFunctionCache { - using IDEProblemType = IDETabulationProblem; - using FlowFunctionPtrType = typename IDEProblemType::FlowFunctionPtrType; - - using n_t = typename AnalysisDomainTy::n_t; - using d_t = typename AnalysisDomainTy::d_t; - using f_t = typename AnalysisDomainTy::f_t; - using t_t = typename AnalysisDomainTy::t_t; - using l_t = typename AnalysisDomainTy::l_t; - - using FlowFunctionType = FlowFunction; - using EdgeFunctionType = EdgeFunction; - +namespace detail { +struct FlowEdgeFunctionCachePerf { PAMM_CATEGORY(FlowEdgeFunctionCache); // NOLINTBEGIN @@ -124,6 +105,29 @@ class FlowEdgeFunctionCache { PAMM_COUNTER(SummaryEF_CacheHit, Full); // NOLINTEND +}; +} // namespace detail + +/** + * This class caches flow and edge functions to avoid their reconstruction. + * When a flow or edge function must be applied to multiple times, a cached + * version is used if existend, otherwise a new one is created and inserted + * into the cache. + */ +template > +class FlowEdgeFunctionCache : private detail::FlowEdgeFunctionCachePerf { + using IDEProblemType = IDETabulationProblem; + using FlowFunctionPtrType = typename IDEProblemType::FlowFunctionPtrType; + + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using f_t = typename AnalysisDomainTy::f_t; + using t_t = typename AnalysisDomainTy::t_t; + using l_t = typename AnalysisDomainTy::l_t; + + using FlowFunctionType = FlowFunction; + using EdgeFunctionType = EdgeFunction; public: // Ctor allows access to the IDEProblem in order to get access to flow and diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index ba82efd3d6..c755406b3d 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -65,16 +65,8 @@ namespace psr { -/// Solves the given IDETabulationProblem as described in the 1996 paper by -/// Sagiv, Horwitz and Reps. To solve the problem, call solve(). Results -/// can then be queried by using resultAt() and resultsAt(). -template , - ICFG ICFGTy = typename AnalysisDomainTy::i_t> -class IDESolver - : public IDESolverAPIMixin> { - friend IDESolverAPIMixin>; - +namespace detail { +struct IDESolverPerf { PAMM_CATEGORY(IDESolver); // NOLINTBEGIN @@ -99,8 +91,20 @@ class IDESolver PAMM_TIMER(DFAPhase1, Full); PAMM_TIMER(DFAPhase2, Full); - // NOLINTEND +}; +} // namespace detail + +/// Solves the given IDETabulationProblem as described in the 1996 paper by +/// Sagiv, Horwitz and Reps. To solve the problem, call solve(). Results +/// can then be queried by using resultAt() and resultsAt(). +template , + ICFG ICFGTy = typename AnalysisDomainTy::i_t> +class IDESolver + : private detail::IDESolverPerf, + public IDESolverAPIMixin> { + friend IDESolverAPIMixin>; public: using ProblemTy = IDETabulationProblem; diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 1bf5f1b798..6f9b3acb60 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -176,6 +176,7 @@ class Counter : private detail::CounterBase, inline explicit Counter( std::source_location Loc = std::source_location::current()) noexcept; + constexpr void add(ptrdiff_t Offset) noexcept { Ctr += Offset; } constexpr void operator++() noexcept { ++Ctr; } constexpr void operator++(int) noexcept { ++Ctr; } constexpr void operator+=(ptrdiff_t Offset) noexcept { Ctr += Offset; } @@ -196,6 +197,7 @@ class Counter : private detail::Qualified { public: using detail::Qualified::qualifier; + LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void add(ptrdiff_t Offset) noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++() noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void operator++(int) noexcept {} LLVM_ATTRIBUTE_ALWAYS_INLINE constexpr void From 16e5bf5ccdf973e4551f16aacb9a6e08b1276cf3 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 27 Jun 2026 15:57:00 +0200 Subject: [PATCH 12/18] Deprecating the PAMM class --- CMakeLists.txt | 6 ++---- .../DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h | 1 - include/phasar/Utils/PAMM.h | 8 +++++++- include/phasar/Utils/PAMMMacros.h | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4177899f46..d0794cec3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,14 +200,12 @@ endif (PHASAR_ENABLE_PIC) set(PHASAR_ENABLE_PAMM "Off" CACHE STRING "Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is 'Off')") set_property(CACHE PHASAR_ENABLE_PAMM PROPERTY STRINGS "Off" "Core" "Full") -if(PHASAR_ENABLE_PAMM STREQUAL "Core" AND NOT PHASAR_BUILD_UNITTESTS) +if(PHASAR_ENABLE_PAMM STREQUAL "Core") set(PAMM_CORE ON) message(STATUS "PAMM metric severity level: Core") -elseif(PHASAR_ENABLE_PAMM STREQUAL "Full" AND NOT PHASAR_BUILD_UNITTESTS) +elseif(PHASAR_ENABLE_PAMM STREQUAL "Full") set(PAMM_FULL ON) message(STATUS "PAMM metric severity level: Full") -elseif(PHASAR_BUILD_UNITTESTS AND (PHASAR_ENABLE_PAMM STREQUAL "Core" OR PHASAR_ENABLE_PAMM STREQUAL "Full")) - message(WARNING "PAMM metric severity level: Off (due to unittests)") else() message(STATUS "PAMM metric severity level: Off") endif() diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index c1f00cb93e..884ddb0280 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -456,7 +456,6 @@ class FlowEdgeFunctionCache : private detail::FlowEdgeFunctionCachePerf { void print() { if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::Full) { - PAMM_GET_INSTANCE; PHASAR_LOG_LEVEL(INFO, "=== Flow-Edge-Function Cache Statistics ==="); PHASAR_LOG_LEVEL(INFO, "Normal-flow function cache hits: " << NormalFF_CacheHit.value()); diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 6f9b3acb60..dc61caf18d 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -567,6 +567,11 @@ void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat); inline void printMeasuredData(llvm::raw_ostream &OS, const pamm::Category &Cat) {} +[[nodiscard]] inline ptrdiff_t +getSumCount(pamm::detail::IsCounter auto const &...Counters) noexcept { + return (Counters.value() + ...); +} + } // namespace pamm /// This class offers functionality to measure different performance metrics. @@ -589,7 +594,8 @@ inline void printMeasuredData(llvm::raw_ostream &OS, /// @note This class implements the Singleton Pattern - use the /// PAMM_GET_INSTANCE macro to retrieve an instance of PAMM before you use any /// other macro from this class. -class PAMM final { +class [[deprecated("Use the new PAMM functionality from the psr::pamm " + "namespace instead")]] PAMM final { public: using TimePoint_t = std::chrono::steady_clock::time_point; using Duration_t = std::chrono::nanoseconds; diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index e4fa7c1ee8..1e1b7f0210 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -80,7 +80,7 @@ #define PRINT_TIMER(TIMER_ID) \ pamm.getPrintableDuration(pamm.elapsedTime(TIMER_ID)) -#define GET_SUM_COUNT(...) pamm.getSumCount(__VA_ARGS__) +#define GET_SUM_COUNT(...) ::psr::pamm::getSumCount(__VA_ARGS__) #define PRINT_MEASURED_DATA(OUTPUT_STREAM) pamm.printMeasuredData(OUTPUT_STREAM) #define EXPORT_MEASURED_DATA(PATH) pamm.exportMeasuredData(PATH) From 89b92c776489fac8cfb795f67a2464e983f54ece Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 27 Jun 2026 17:16:21 +0200 Subject: [PATCH 13/18] Add some thread-safety + comments --- CMakeLists.txt | 1 + config.h.in | 1 + include/phasar/Utils/PAMM.h | 270 +++++++++++++++++++++++++----- include/phasar/Utils/PAMMMacros.h | 1 + lib/Utils/PAMM.cpp | 14 +- 5 files changed, 241 insertions(+), 46 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d0794cec3f..274bf0fb81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,6 +199,7 @@ endif (PHASAR_ENABLE_PIC) # PAMM set(PHASAR_ENABLE_PAMM "Off" CACHE STRING "Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is 'Off')") set_property(CACHE PHASAR_ENABLE_PAMM PROPERTY STRINGS "Off" "Core" "Full") +option(PHASAR_THREAD_SAFE_PAMM "Use a thread-safe version of PAMM, if PAMM is enabled" ON) if(PHASAR_ENABLE_PAMM STREQUAL "Core") set(PAMM_CORE ON) diff --git a/config.h.in b/config.h.in index 926d69b0bf..b8d5423b9a 100644 --- a/config.h.in +++ b/config.h.in @@ -7,6 +7,7 @@ #cmakedefine PAMM_CORE #cmakedefine PAMM_FULL +#cmakedefine PHASAR_THREAD_SAFE_PAMM #cmakedefine DYNAMIC_LOG #cmakedefine BUILD_PHASAR_CLANG diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index dc61caf18d..ad2871f0aa 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -10,7 +10,6 @@ *****************************************************************************/ #include "phasar/Config/phasar-config.h" -#include "phasar/Utils/Average.h" #include "phasar/Utils/ChronoUtils.h" #include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/TemplateString.h" @@ -37,10 +36,45 @@ #include #include +#ifdef PHASAR_THREAD_SAFE_PAMM +#include +#include +#else +#include "phasar/Utils/Average.h" +#endif + namespace llvm { class raw_ostream; } // namespace llvm +/// \file +/// PhASAR's performance measurement mechanism (PAMM). +/// Provides counters, histograms, and timers for fine-grained performance +/// tracking. Use the macros from PAMMMacros.h to declare static instances +/// of these metrics. On construction, they are automatically registered in the +/// static pamm::Registry, so that report-generation can be automated. +/// The macros expect a valid C++ identifier as name, and a severify-level +/// (Core/Full) under which the metric should be active. +/// +/// Metrics are categorizable via a pamm::Category. The +/// performance report groups by category; categories can be enabled/disabled at +/// runtime. By convention, the macros register the metrics under the category +/// 'PAMMCategory'; create a new category with PAMM_CATEGORY(name). +/// +/// Currently, PAMM can be run with all metrics (severity level 2 = Full) or +/// only core metrics (severity level 1 = Core) enabled. The severity level can +/// be changed when building PhASAR. The CMake option is +/// -DPHASAR_ENABLE_PAMM=[Off/Core/Full]. +/// +/// Thread-safety: +/// If PhASAR is configured with the cmake-option -DPHASAR_THREAD_SAFE_PAMM=ON, +/// PAMM provides some thread-safety guarentees. Generally, construction and +/// destruction of metrics is NOT thread-safe. The pamm::Registry is therefore +/// also NOT thread-safe. Adding data, i.e, incrementing counters, adding to +/// histograms, starting/stopping timers is protected by suitable +/// synchronization primitives and can therefore be safely performed +/// concurrently. + namespace psr { /// Defines the different level of severity of PAMM's performance evaluation @@ -71,14 +105,21 @@ class Category { [[nodiscard]] constexpr llvm::StringRef name() const noexcept { return Name; } - [[nodiscard]] constexpr auto isEnabled() const noexcept { return IsEnabled; } + [[nodiscard]] constexpr bool isEnabled() const noexcept { + return IsEnabled.load(std::memory_order_relaxed); + } constexpr void disable() noexcept { IsEnabled = false; } constexpr void enable() noexcept { IsEnabled = true; } private: llvm::StringRef Name; - bool IsEnabled = true; +#ifdef PHASAR_THREAD_SAFE_PAMM + std::atomic_bool +#else + bool +#endif + IsEnabled = true; }; template <> class Category { @@ -111,38 +152,172 @@ namespace detail { struct PAMMBase { std::source_location Loc{}; }; +#ifdef PHASAR_THREAD_SAFE_PAMM +struct CounterBase : PAMMBase { + std::atomic_ptrdiff_t Ctr{}; + + constexpr void add(ptrdiff_t Offset) noexcept { + Ctr.fetch_add(Offset, std::memory_order_relaxed); + } + + [[nodiscard]] constexpr ptrdiff_t value() const noexcept { + return Ctr.load(std::memory_order_relaxed); + } +}; + +struct MinMaxCounterBase : PAMMBase { + std::atomic_size_t Min = SIZE_MAX; + std::atomic_size_t Max = 0; + std::atomic_size_t Sum{}; + std::atomic_size_t NumSamples{}; + + constexpr void add(size_t Offset) noexcept { + Sum.fetch_add(Offset, std::memory_order_relaxed); + NumSamples.fetch_add(1, std::memory_order_relaxed); + + { + auto PrevMin = Min.load(std::memory_order_relaxed); + while (PrevMin > Offset && + Min.compare_exchange_weak(PrevMin, Offset, + std::memory_order_relaxed)) { + } + } + { + auto PrevMax = Max.load(std::memory_order_relaxed); + while (PrevMax < Offset && + Max.compare_exchange_weak(PrevMax, Offset, + std::memory_order_relaxed)) { + } + } + }; + + [[nodiscard]] double getAverage() noexcept { + return double(Sum.load(std::memory_order_relaxed)) / + double(getNumSamples()); + } + [[nodiscard]] size_t getNumSamples() noexcept { + return NumSamples.load(std::memory_order_relaxed); + } + + void clear() noexcept { + Min = SIZE_MAX; + Max = 0; + Sum = 0; + NumSamples = 0; + } +}; + +struct TimerBase : PAMMBase { + + static constexpr std::chrono::steady_clock::time_point None = + std::chrono::steady_clock::time_point::max(); + + std::atomic StartPoint = None; + // no atomic arithmetic with nanoseconds, so use int64_t here: + std::atomic Acc{}; + + [[nodiscard]] constexpr bool isStarted() const noexcept { + // XXX: Revisit the memory-order here: Can it be relaxed? + return StartPoint.load(std::memory_order_acquire) != None; + } + + void start() noexcept { + assert(!isStarted() && "Starting an already running timer is not allowed"); + StartPoint.store(std::chrono::steady_clock::now(), + std::memory_order_release); + } + + void stop() noexcept { + auto EndPoint = std::chrono::steady_clock::now(); + auto StartPoint = + this->StartPoint.exchange(None, std::memory_order_acq_rel); + assert(StartPoint != None && "Stopping a not-running timer is not allowed"); + Acc.fetch_add((EndPoint - StartPoint).count(), std::memory_order_relaxed); + } + + void reset() noexcept { + Acc = 0; + StartPoint = None; + } + + [[nodiscard]] constexpr auto elapsedNanos() const noexcept { + return std::chrono::nanoseconds(Acc.load(std::memory_order_relaxed)); + } + + [[nodiscard]] constexpr std::chrono::nanoseconds + pendingNanos() const noexcept { + auto StartPoint = this->StartPoint.load(std::memory_order_relaxed); + if (StartPoint == None) { + return {}; + } + auto EndPoint = std::chrono::steady_clock::now(); + return EndPoint - StartPoint; + } + + [[nodiscard]] hms elapsed() const noexcept { return elapsedNanos(); } +}; + +#else struct CounterBase : PAMMBase { ptrdiff_t Ctr{}; - // TODO: Add thread-safe counter + + constexpr void add(ptrdiff_t Offset) noexcept { Ctr += Offset; } + + [[nodiscard]] constexpr ptrdiff_t value() const noexcept { return Ctr; } }; + struct MinMaxCounterBase : PAMMBase { size_t Min = SIZE_MAX; size_t Max = 0; Sampler Avg{}; -}; -struct HistogramBase : PAMMBase { - llvm::StringMap HistData{}; + constexpr void add(size_t Offset) noexcept { + Avg.addSample(Offset); + if (Offset > Max) { + Max = Offset; + } + if (Offset < Min) { + Min = Offset; + } + }; + + [[nodiscard]] double getAverage() noexcept { return Avg.getAverage(); } + [[nodiscard]] size_t getNumSamples() noexcept { return Avg.getNumSamples(); } + + void clear() noexcept { + Min = SIZE_MAX; + Max = 0; + Avg = {}; + } }; + struct TimerBase : PAMMBase { - std::optional Tm{}; + + static constexpr std::chrono::steady_clock::time_point None = + std::chrono::steady_clock::time_point::max(); + + std::chrono::steady_clock::time_point StartPoint = None; std::chrono::nanoseconds Acc{}; + [[nodiscard]] constexpr bool isStarted() const noexcept { + return StartPoint != None; + } + void start() noexcept { - assert(!Tm.has_value() && - "Starting an already running timer is not allowed"); - Tm.emplace(); + assert(!isStarted() && "Starting an already running timer is not allowed"); + StartPoint = std::chrono::steady_clock::now(); } void stop() noexcept { - assert(Tm.has_value() && "Stopping a not-running timer is not allowed"); - Acc += Tm->elapsedNanos(); - Tm.reset(); + assert(isStarted() && "Stopping a not-running timer is not allowed"); + auto EndPoint = std::chrono::steady_clock::now(); + Acc += (EndPoint - StartPoint); + StartPoint = None; } void reset() noexcept { Acc = {}; - Tm.reset(); + StartPoint = None; } [[nodiscard]] constexpr std::chrono::nanoseconds @@ -150,9 +325,27 @@ struct TimerBase : PAMMBase { return Acc; } + [[nodiscard]] constexpr std::chrono::nanoseconds + pendingNanos() const noexcept { + if (!isStarted()) { + return {}; + } + auto EndPoint = std::chrono::steady_clock::now(); + return EndPoints - StartPoint; + } + [[nodiscard]] hms elapsed() const noexcept { return Acc; } }; +#endif + +struct HistogramBase : PAMMBase { + llvm::StringMap HistData{}; +#ifdef PHASAR_THREAD_SAFE_PAMM + std::mutex Mx{}; +#endif +}; + template struct Qualified { [[nodiscard]] constexpr std::string qualifier() const { auto Ret = std::string(Cat->name()); @@ -176,18 +369,19 @@ class Counter : private detail::CounterBase, inline explicit Counter( std::source_location Loc = std::source_location::current()) noexcept; - constexpr void add(ptrdiff_t Offset) noexcept { Ctr += Offset; } - constexpr void operator++() noexcept { ++Ctr; } - constexpr void operator++(int) noexcept { ++Ctr; } - constexpr void operator+=(ptrdiff_t Offset) noexcept { Ctr += Offset; } - constexpr void operator-=(ptrdiff_t Offset) noexcept { Ctr -= Offset; } + using detail::CounterBase::add; + using detail::CounterBase::value; - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Counter C) { + constexpr void operator++() noexcept { add(1); } + constexpr void operator++(int) noexcept { add(1); } + constexpr void operator+=(ptrdiff_t Offset) noexcept { add(Offset); } + constexpr void operator-=(ptrdiff_t Offset) noexcept { add(-Offset); } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const Counter &C) { return OS << Cat->name() << "::" << Name << ": " << C.Ctr; } - [[nodiscard]] constexpr ptrdiff_t value() const noexcept { return Ctr; } - private: [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } }; @@ -217,13 +411,14 @@ class Counter : private detail::Qualified { namespace detail { struct IsCounterImpl { template *Cat> - static std::true_type test(Counter); + static std::true_type test(const Counter &); static std::false_type test(...); }; template -concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; +concept IsCounter = + decltype(IsCounterImpl::test(std::declval()))::value; } // namespace detail template *Cat> @@ -237,24 +432,17 @@ class MinMaxCounter : private detail::MinMaxCounterBase, inline explicit MinMaxCounter( std::source_location Loc = std::source_location::current()) noexcept; - constexpr void add(size_t Offset) noexcept { - Avg.addSample(Offset); - if (Offset > Max) { - Max = Offset; - } - if (Offset < Min) { - Min = Offset; - } - }; + using detail::MinMaxCounterBase::add; constexpr void operator++() noexcept { add(1); } constexpr void operator++(int) noexcept { add(1); } constexpr void operator+=(size_t Offset) noexcept { add(Offset); } - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, MinMaxCounter C) { + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const MinMaxCounter &C) { return OS << Cat->name() << "::" << Name << ": min(" << C.Min << "), max(" - << C.Max << "), avg: " << llvm::format("%g", C.Avg.getAverage()) - << ", #samples(" << C.Avg.getNumSamples() << ')'; + << C.Max << "), avg: " << llvm::format("%g", C.getAverage()) + << ", #samples(" << C.getNumSamples() << ')'; } private: @@ -294,6 +482,9 @@ class Histogram : private detail::HistogramBase, const Histogram &H) { OS << Cat->name() << "::" << Name << ":\n"; OS << "Value\t| #Occurrences\n"; +#ifdef PHASAR_THREAD_SAFE_PAMM + std::lock_guard Lck(H.Mx); +#endif for (const auto &[Dat, Val] : H.HistData) { OS << Dat << "\t| " << Val << '\n'; } @@ -302,6 +493,9 @@ class Histogram : private detail::HistogramBase, void add(llvm::StringRef DataPointId, uint64_t Increment) { if (Cat->isEnabled()) { +#ifdef PHASAR_THREAD_SAFE_PAMM + std::lock_guard Lck(Mx); +#endif this->HistData[DataPointId] += Increment; } } @@ -397,7 +591,7 @@ template class ScopedTimer { } ~ScopedTimer() { - if (Tm->Tm.has_value()) { + if (Tm->isStarted()) { Tm->stop(); } } diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index 1e1b7f0210..1d7f2d42c3 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -21,6 +21,7 @@ #include "phasar/Utils/Macros.h" #include "phasar/Utils/PAMM.h" +// optional second parameter: boolean whether the category is enabled by default #define PAMM_CATEGORY(NAME, ...) \ static inline ::psr::pamm::Category<> PAMMCategory { \ #NAME __VA_OPT__(, ) __VA_ARGS__ \ diff --git a/lib/Utils/PAMM.cpp b/lib/Utils/PAMM.cpp index 9b4ccacbd3..b4b9f58fe4 100644 --- a/lib/Utils/PAMM.cpp +++ b/lib/Utils/PAMM.cpp @@ -99,8 +99,8 @@ static void printMMCountersImpl( OS << Cat.name() << ":\n"; for (const auto &[Name, C] : CatCtrs) { OS << " " << Name << ": min(" << C->Min << "), max(" << C->Max - << "), avg: " << llvm::format("%g", C->Avg.getAverage()) << ", #samples(" - << C->Avg.getNumSamples() << ")\n"; + << "), avg: " << llvm::format("%g", C->getAverage()) << ", #samples(" + << C->getNumSamples() << ")\n"; } OS << '\n'; } @@ -155,11 +155,11 @@ static void printTimersImpl( const llvm::DenseMap &CatTms) { OS << Cat.name() << ":\n"; for (const auto &[Name, Tm] : CatTms) { - auto Time = Tm->Acc; - bool StillRunning = Tm->Tm.has_value(); + auto Time = Tm->elapsedNanos(); + bool StillRunning = Tm->isStarted(); OS << " " << Name << ":\t"; if (StillRunning) { - Time += Tm->Tm->elapsedNanos(); + Time += Tm->pendingNanos(); OS << hms{Time} << " (still running)\n"; } else { OS << hms{Time} << '\n'; @@ -217,9 +217,7 @@ void pamm::Registry::reset() noexcept { for (const auto &[Cat, Ctrs] : MMCounters) { for (const auto &[_, Ctr] : Ctrs) { - Ctr->Min = SIZE_MAX; - Ctr->Max = 0; - Ctr->Avg = {}; + Ctr->clear(); } } From ef2a9aec26a6f443daab6842894fbee8514c0c0e Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Sat, 4 Jul 2026 19:06:25 +0200 Subject: [PATCH 14/18] minor --- include/phasar/Utils/PAMM.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index ad2871f0aa..39b96fa83c 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -13,7 +13,6 @@ #include "phasar/Utils/ChronoUtils.h" #include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/TemplateString.h" -#include "phasar/Utils/Timer.h" #include "phasar/Utils/TypeTraits.h" #include "phasar/Utils/Utilities.h" From f9362b7fd193e509d6e8d169bfab5e214c71161f Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 13 Jul 2026 18:13:03 +0200 Subject: [PATCH 15/18] Fix PAMM without thread-safety --- include/phasar/Utils/PAMM.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 39b96fa83c..302083e89f 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -105,7 +105,11 @@ class Category { [[nodiscard]] constexpr llvm::StringRef name() const noexcept { return Name; } [[nodiscard]] constexpr bool isEnabled() const noexcept { - return IsEnabled.load(std::memory_order_relaxed); + return IsEnabled +#ifdef PHASAR_THREAD_SAFE_PAMM + .load(std::memory_order_relaxed) +#endif + ; } constexpr void disable() noexcept { IsEnabled = false; } @@ -190,11 +194,11 @@ struct MinMaxCounterBase : PAMMBase { } }; - [[nodiscard]] double getAverage() noexcept { + [[nodiscard]] double getAverage() const noexcept { return double(Sum.load(std::memory_order_relaxed)) / double(getNumSamples()); } - [[nodiscard]] size_t getNumSamples() noexcept { + [[nodiscard]] size_t getNumSamples() const noexcept { return NumSamples.load(std::memory_order_relaxed); } @@ -280,8 +284,10 @@ struct MinMaxCounterBase : PAMMBase { } }; - [[nodiscard]] double getAverage() noexcept { return Avg.getAverage(); } - [[nodiscard]] size_t getNumSamples() noexcept { return Avg.getNumSamples(); } + [[nodiscard]] double getAverage() const noexcept { return Avg.getAverage(); } + [[nodiscard]] size_t getNumSamples() const noexcept { + return Avg.getNumSamples(); + } void clear() noexcept { Min = SIZE_MAX; @@ -330,7 +336,7 @@ struct TimerBase : PAMMBase { return {}; } auto EndPoint = std::chrono::steady_clock::now(); - return EndPoints - StartPoint; + return EndPoint - StartPoint; } [[nodiscard]] hms elapsed() const noexcept { return Acc; } From 098648136441bf74873f5213b41c6e9c1715c481 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 13 Jul 2026 19:30:29 +0200 Subject: [PATCH 16/18] Fix PAMM usage in merged-in CFLFieldSensIFDSProblem + fix Core sev level --- .../IfdsIde/CFLFieldSensIFDSProblem.h | 3 - include/phasar/Utils/PAMM.h | 63 +++++++++++------ .../IfdsIde/CFLFieldSensIFDSProblem.cpp | 68 +++++++++---------- 3 files changed, 75 insertions(+), 59 deletions(-) diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h index 9ed5419ced..2a9d80a076 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h @@ -517,7 +517,6 @@ class CFLFieldSensIFDSProblem UserProblem->getZeroValue()), UserProblem(UserProblem), Config(std::move(Config)) { Mgr.reserve(UserProblem->getProjectIRDB()->getNumInstructions()); - regCounters(); } /// Constructs an IDETabulationProblem with the usual arguments, forwarded @@ -620,8 +619,6 @@ class CFLFieldSensIFDSProblem [[nodiscard]] EFResultPtr makeEFPtr(cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF); - static void regCounters() noexcept; - IFDSTabulationProblem *UserProblem{}; cfl_fieldsens::FieldStringManager Mgr{}; cfl_fieldsens::IFDSProblemConfig Config{}; diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 302083e89f..1e89f865cc 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -151,6 +151,14 @@ template <> class Category { llvm::StringRef Name; }; +template +concept IsCategory = + std::same_as> || std::same_as>; + +template +concept IsCategoryVar = + IsCategory>>; + namespace detail { struct PAMMBase { std::source_location Loc{}; @@ -363,7 +371,8 @@ template struct Qualified { class Registry; -template *Cat> +template + requires IsCategoryVar class Counter : private detail::CounterBase, private detail::Qualified { friend Registry; @@ -391,7 +400,8 @@ class Counter : private detail::CounterBase, [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } }; -template *Cat> +template + requires IsCategoryVar class Counter : private detail::Qualified { public: using detail::Qualified::qualifier; @@ -415,7 +425,8 @@ class Counter : private detail::Qualified { namespace detail { struct IsCounterImpl { - template *Cat> + template + requires IsCategoryVar static std::true_type test(const Counter &); static std::false_type test(...); @@ -426,7 +437,8 @@ concept IsCounter = decltype(IsCounterImpl::test(std::declval()))::value; } // namespace detail -template *Cat> +template + requires IsCategoryVar class MinMaxCounter : private detail::MinMaxCounterBase, private detail::Qualified { friend Registry; @@ -454,7 +466,8 @@ class MinMaxCounter : private detail::MinMaxCounterBase, [[nodiscard]] constexpr detail::CounterBase *base() noexcept { return this; } }; -template *Cat> +template + requires IsCategoryVar class MinMaxCounter : private detail::Qualified { public: using detail::Qualified::qualifier; @@ -472,7 +485,8 @@ class MinMaxCounter : private detail::Qualified { } }; -template *Cat> +template + requires IsCategoryVar class Histogram : private detail::HistogramBase, private detail::Qualified { friend Registry; @@ -521,7 +535,8 @@ class Histogram : private detail::HistogramBase, } }; -template *Cat> +template + requires IsCategoryVar class Histogram : private detail::Qualified { friend Registry; @@ -541,7 +556,8 @@ class Histogram : private detail::Qualified { uint64_t /*Increment*/) {} }; -template *Cat> +template + requires IsCategoryVar class Timer : private detail::TimerBase, private detail::Qualified { friend Registry; template friend class ScopedTimer; @@ -566,7 +582,8 @@ class Timer : private detail::TimerBase, private detail::Qualified { [[nodiscard]] constexpr detail::TimerBase *base() noexcept { return this; } }; -template *Cat> +template + requires IsCategoryVar class Timer : private detail::Qualified { public: using detail::Qualified::qualifier; @@ -588,7 +605,7 @@ class Timer : private detail::Qualified { template class ScopedTimer { public: - template *Cat> + template constexpr ScopedTimer(Timer &Tm) : Tm(&Tm) { if (Cat->isEnabled()) { Tm.start(); @@ -612,18 +629,22 @@ template class ScopedTimer { template <> class ScopedTimer { public: - template *Cat> + template constexpr ScopedTimer(Timer &Tm) {} }; class Registry { - template *Cat> + template + requires IsCategoryVar friend class Counter; - template *Cat> + template + requires IsCategoryVar friend class MinMaxCounter; - template *Cat> + template + requires IsCategoryVar friend class Histogram; - template *Cat> + template + requires IsCategoryVar friend class Timer; public: @@ -726,14 +747,16 @@ class Registry { llvm::StringMap *> RegisteredCategories; }; -template *Cat> +template + requires IsCategoryVar inline Counter::Counter(std::source_location Loc) noexcept { static_assert(Cat != nullptr); this->Loc = Loc; Registry::instance().registerCounter(this, Loc); } -template *Cat> +template + requires IsCategoryVar inline MinMaxCounter::MinMaxCounter( std::source_location Loc) noexcept { static_assert(Cat != nullptr); @@ -741,7 +764,8 @@ inline MinMaxCounter::MinMaxCounter( Registry::instance().registerMMCounter(this, Loc); } -template *Cat> +template + requires IsCategoryVar inline Histogram::Histogram( std::source_location Loc) noexcept { static_assert(Cat != nullptr); @@ -749,7 +773,8 @@ inline Histogram::Histogram( Registry::instance().registerHistogram(this, Loc); } -template *Cat> +template + requires IsCategoryVar inline Timer::Timer(std::source_location Loc) noexcept { static_assert(Cat != nullptr); this->Loc = Loc; diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.cpp index f02934c5db..a383dfd698 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.cpp @@ -30,6 +30,25 @@ using namespace psr; using namespace psr::cfl_fieldsens; +namespace psr::cfl_fieldsens { +PAMM_CATEGORY(CFLFieldSens); + +PAMM_COUNTER(ExtendCacheRefs, Full); +PAMM_COUNTER(ExtendCacheMisses, Full); +PAMM_COUNTER(CombineCacheRefs, Full); +PAMM_COUNTER(CombineCacheMisses, Full); +PAMM_COUNTER(CombineCallsTotal, Full); +PAMM_COUNTER(CombineLIdentity, Full); +PAMM_COUNTER(CombineLIdentitySlow, Full); +PAMM_COUNTER(CombineRIdentity, Full); +PAMM_COUNTER(CombineRIdentitySlow, Full); + +PAMM_COUNTER(GetResultEFTop, Full); +PAMM_COUNTER(GetResultEFBot, Full); +PAMM_COUNTER(GetResultEFPtr, Full); + +} // namespace psr::cfl_fieldsens + FieldStringManager::FieldStringManager() { // Sentinel NodeCompressor.insertDummy( @@ -749,10 +768,9 @@ allTopPtr() noexcept { [[nodiscard]] static EdgeFunction getResultEF(llvm::PointerIntPair Ptr) noexcept { - PAMM_GET_INSTANCE; switch (Ptr.getInt()) { [[likely]] case AllEFPtrId: - INC_COUNTER("getResultEF Ptr", 1, Full); + GetResultEFPtr++; assert(Ptr.getPointer() != nullptr); assert(Ptr.getPointer() == Ptr.getOpaqueValue() && "Zero-tag does not pollute the alignment bits"); @@ -760,35 +778,16 @@ getResultEF(llvm::PointerIntPair static_cast( Ptr.getOpaqueValue())}; case AllBottomId: - INC_COUNTER("getResultEF Bot", 1, Full); + GetResultEFBot++; return AllBottom{}; case AllTopId: - INC_COUNTER("getResultEF Top", 1, Full); + GetResultEFTop++; return AllTop{}; default: llvm_unreachable("All valid tags should be handled explicitly"); } } -void CFLFieldSensIFDSProblem::regCounters() noexcept { - PAMM_GET_INSTANCE; - - REG_COUNTER("ExtendCache Refs", 0, Full); - REG_COUNTER("ExtendCache Misses", 0, Full); - - REG_COUNTER("CombineCache Refs", 0, Full); - REG_COUNTER("CombineCache Misses", 0, Full); - REG_COUNTER("Combine CallsTotal", 0, Full); - REG_COUNTER("Combine LIdentity", 0, Full); - REG_COUNTER("Combine LIdentitySlow", 0, Full); - REG_COUNTER("Combine RIdentity", 0, Full); - REG_COUNTER("Combine RIdentitySlow", 0, Full); - - REG_COUNTER("getResultEF Top", 0, Full); - REG_COUNTER("getResultEF Bot", 0, Full); - REG_COUNTER("getResultEF Ptr", 0, Full); -} - auto CFLFieldSensIFDSProblem::extend(const EdgeFunction &L, const EdgeFunction &R) -> EdgeFunction { @@ -811,13 +810,11 @@ auto CFLFieldSensIFDSProblem::extend(const EdgeFunction &L, return L; } - PAMM_GET_INSTANCE; - - INC_COUNTER("ExtendCache Refs", 1, Full); + ExtendCacheRefs++; auto [It, Inserted] = ExtendCache.try_emplace( std::pair{FldSensL->Impl, FldSensR->Impl}, lazy{[&]() -> EFResultPtr { - INC_COUNTER("ExtendCache Misses", 1, Full); + ExtendCacheMisses++; auto Txn = FldSensL->Impl->Transform; Txn.applyTransforms(FldSensR->Impl->Transform, DepthKLimit); @@ -855,21 +852,18 @@ auto CFLFieldSensIFDSProblem::combine(const EdgeFunction &L, return Dflt; } auto Ret = [&]() -> EdgeFunction { - PAMM_GET_INSTANCE; - INC_COUNTER("Combine CallsTotal", 1, Full); + CombineCallsTotal++; const auto *FldSensL = L.dyn_cast(); const auto *FldSensR = R.dyn_cast(); if (FldSensL) { if (FldSensR) { - - INC_COUNTER("CombineCache Refs", 1, Full); + CombineCacheRefs++; auto [CacheIt, CacheInserted] = CombineCache.try_emplace( psr::minmaxVal(FldSensL->Impl, FldSensR->Impl), lazy{[this, FldSensL{*FldSensL}, FldSensR{*FldSensR}]() -> EFResultPtr { - PAMM_GET_INSTANCE; - INC_COUNTER("CombineCache Misses", 1, Full); + CombineCacheMisses++; // A complicated way of expressing set-union of LPaths and RPaths. // Reason being that we don't want to unnecessarily copy the sets. @@ -915,24 +909,24 @@ auto CFLFieldSensIFDSProblem::combine(const EdgeFunction &L, } if (R.isa>()) { - INC_COUNTER("Combine RIdentity", 1, Full); + CombineRIdentity++; if (FldSensL->Impl->Transform.Paths.contains(AccessPath{})) { return L; } - INC_COUNTER("Combine RIdentitySlow", 1, Full); + CombineRIdentitySlow++; auto Txn = FldSensL->Impl->Transform; Txn.Paths.insert(AccessPath{}); return makeEF( CFLFieldSensEdgeFunctionImpl::from(std::move(Txn), DepthKLimit)); } } else if (FldSensR && L.isa>()) { - INC_COUNTER("Combine LIdentity", 1, Full); + CombineLIdentity++; if (FldSensR->Impl->Transform.Paths.contains(AccessPath{})) { return R; } - INC_COUNTER("Combine LIdentitySlow", 1, Full); + CombineLIdentitySlow++; auto Txn = FldSensR->Impl->Transform; Txn.Paths.insert(AccessPath{}); return makeEF( From 57277d79e21a2a2b4adc4c5c6629906348380067 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 13 Jul 2026 20:11:56 +0200 Subject: [PATCH 17/18] Attempt to fix release builds in CI with ccache --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eb0f1d5b1..eea3c23168 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,7 @@ jobs: -DBUILD_PHASAR_CLANG=OFF \ -DPHASAR_USE_Z3=ON \ -DPHASAR_BUILD_MODULES=ON \ + -DPHASAR_TARGET_ARCH="" \ -DPHASAR_LLVM_VERSION=${{ matrix.llvm-version }} \ ${{ matrix.flags }} \ -G Ninja From 77c0172fb5b9bcc3ae673f0ac32695f59f48b362 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 13 Jul 2026 20:21:04 +0200 Subject: [PATCH 18/18] Try to use newer clang to prevent internal compiler error with clang-20 in release mode --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eea3c23168..7f1a4fd25d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-24.04, ubuntu-24.04-arm] - compiler: [ [clang++-20, clang-20, "clang-20 libclang-rt-20-dev clang-tools-20"] ] + compiler: [ [clang++-21, clang-21, "clang-21 libclang-rt-21-dev clang-tools-21"] ] build: [ Debug, Release, DebugLibdeps, DebugCov ] llvm-version: [ 16, "22.1" ] include: @@ -31,7 +31,7 @@ jobs: - build: DebugCov cmake_build_type: Debug flags: -DCODE_COVERAGE=ON - extra_dependencies: llvm-20 # For coverage + extra_dependencies: llvm-21 # For coverage - llvm-version: 16 llvm-major-version: 16 - llvm-version: "22.1"