From 8a786aca8263ee741a88f62cc4b5b2575e394446 Mon Sep 17 00:00:00 2001 From: Sriteja Kummita <38907381+sritejakv@users.noreply.github.com> Date: Sun, 25 Feb 2024 15:18:07 +0100 Subject: [PATCH] Implementing On-The-Fly Reporting (#701) * Analysis Printer (#17) * Initial Commit * AnalysisPrinter Second commit * Initial Commit * Integrate Printer with client Analysis and test * Addressing Review comments * Integrate AnalysisPrinter with all analyses and template class modified * vector emplace_back instead of push_back * Testcase for AnalysisPrinter * GroundTruth derived class initial commit * AnalysisPrinter Test complete and Test * fixing myphasartool file * Test pre-commit fix * Adding Test cases and fixing PR failure * 1.template params to N,D,L 2.remove AnalysisType param from AnalysisResults 3.rearranging class variables * 1.template params to N,D,L 2.remove AnalysisType param from AnalysisResults 3.rearranging class variables * Null AnalysisPrinter singleton * Adding AnalysisPrinter to IDETabulation Problem * making free (N,D,L)ToString functions * disable copy and move for analysis-printer * Default NullAnalysisPrinter and explicit print methods * removing SetAnalysisPrinter from client analyses and modified Testcase for AnalysisPrinter * Adding superclass for AnalysisPrinter * Addressing review comments and fixing PR build failure * fix: minors * fix: minor (clang-tidy) * fix: review feedback * misc: minor refactoring --------- Co-authored-by: SanthoshMohan Co-authored-by: Sriteja Kummita * OnTheFlyReporting Initial Commit * fix: review feedback * onTheFlyAnalysis makeUniquePtr * OnTheFlyReporting Initial Commit * onTheFlyAnalysis makeUniquePtr * addressing minor error in prev commit * Refactoring Warn Variable and Refactoring lambda flow for AnalysisPrinter * Integrating sourceMgr to AnalysisPrinter * Testcase for OnTheFlyAnalysisPrinting * Testcase for SourceMgrPrinter * MaybeUniquePtr for SourceMgrPrinter * Minor review comments * refactoring the printers * adding TODOs * more TODOs * Refactor AnalysisPrinter part1 * Refactor AnalysisPrinter part2 * dev: update AnalysisPrinterBase, integrate and fix tests * adding warning kind * fix ci * update codeowners for analysis-printer * Refactor SourceManagerPrinter * Minor in OTF Printer Test * fixing review feedback * fix l_t printing * Mandating analysisType to tyestate descriptions --------- Co-authored-by: SanthoshMohan Co-authored-by: Fabian Schiebel --- .clang-tidy | 1 + .github/CODEOWNERS | 6 + .../DataFlow/IfdsIde/IDETabulationProblem.h | 2 +- .../IfdsIde/Problems/IDETypeStateAnalysis.h | 13 +- .../CSTDFILEIOTypeStateDescription.h | 1 + .../OpenSSLEVPKDFCTXDescription.h | 1 + .../OpenSSLEVPKDFDescription.h | 2 + .../OpenSSLSecureHeapDescription.h | 1 + .../OpenSSLSecureMemoryDescription.h | 1 + .../TypeStateDescription.h | 3 + .../PhasarLLVM/Utils/AnalysisPrinterBase.h | 44 ------- .../PhasarLLVM/Utils/DefaultAnalysisPrinter.h | 47 ------- include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h | 11 ++ .../PhasarLLVM/Utils/LLVMSourceManager.h | 36 ++++++ .../PhasarLLVM/Utils/SourceMgrPrinter.h | 71 +++++++++++ include/phasar/Utils/AnalysisPrinterBase.h | 54 ++++++++ include/phasar/Utils/DefaultAnalysisPrinter.h | 73 +++++++++++ .../Utils/NullAnalysisPrinter.h | 13 +- .../phasar/Utils/OnTheFlyAnalysisPrinter.h | 49 ++++++++ .../Problems/IDEExtendedTaintAnalysis.cpp | 13 +- .../IfdsIde/Problems/IFDSTaintAnalysis.cpp | 13 +- .../Problems/IFDSUninitializedVariables.cpp | 51 ++++---- .../CSTDFILEIOTypeStateDescription.cpp | 5 + .../OpenSSLEVPKDFCTXDescription.cpp | 5 + .../OpenSSLEVPKDFDescription.cpp | 5 + .../OpenSSLSecureHeapDescription.cpp | 5 + .../OpenSSLSecureMemoryDescription.cpp | 5 + lib/PhasarLLVM/Utils/LLVMIRToSrc.cpp | 118 +++++++++++------- lib/PhasarLLVM/Utils/LLVMSourceManager.cpp | 54 ++++++++ lib/PhasarLLVM/Utils/SourceMgrPrinter.cpp | 16 +++ .../ControlFlow/LLVMBasedICFGExportTest.cpp | 29 +++-- unittests/Utils/AnalysisPrinterTest.cpp | 20 +-- unittests/Utils/CMakeLists.txt | 2 + .../Utils/OnTheFlyAnalysisPrinterTest.cpp | 105 ++++++++++++++++ unittests/Utils/SourceMgrPrinterTest.cpp | 95 ++++++++++++++ 35 files changed, 757 insertions(+), 213 deletions(-) delete mode 100644 include/phasar/PhasarLLVM/Utils/AnalysisPrinterBase.h delete mode 100644 include/phasar/PhasarLLVM/Utils/DefaultAnalysisPrinter.h create mode 100644 include/phasar/PhasarLLVM/Utils/LLVMSourceManager.h create mode 100644 include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h create mode 100644 include/phasar/Utils/AnalysisPrinterBase.h create mode 100644 include/phasar/Utils/DefaultAnalysisPrinter.h rename include/phasar/{PhasarLLVM => }/Utils/NullAnalysisPrinter.h (58%) create mode 100644 include/phasar/Utils/OnTheFlyAnalysisPrinter.h create mode 100644 lib/PhasarLLVM/Utils/LLVMSourceManager.cpp create mode 100644 lib/PhasarLLVM/Utils/SourceMgrPrinter.cpp create mode 100644 unittests/Utils/OnTheFlyAnalysisPrinterTest.cpp create mode 100644 unittests/Utils/SourceMgrPrinterTest.cpp diff --git a/.clang-tidy b/.clang-tidy index 8447efed9..54f04105c 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -4,6 +4,7 @@ Checks: '-*, misc-*, -misc-non-private-member-variables-in-classes, -misc-no-recursion, + -misc-use-anonymous-namespace, readability-*, -readability-function-cognitive-complexity, -readability-else-after*, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bfd339c16..3e7412921 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -26,3 +26,9 @@ Dockerfile @janniclas /.docker/ @janniclas /include/phasar/Utils/Logger.h @MMory +/include/phasar/Utils/AnalysisPrinterBase.h @sritejakv +/include/phasar/Utils/DefaultAnalysisPrinter.h @sritejakv +/include/phasar/Utils/NullAnalysisPrinter.h @sritejakv +/include/phasar/Utils/OnTheFlyAnalysisPrinter.h @sritejakv +/include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h @sritejakv +/lib/PhasarLLVM/Utils/SourceMgrPrinter.cpp @sritejakv diff --git a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h index 35bd80ae5..d3c75c1ae 100644 --- a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h +++ b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h @@ -19,8 +19,8 @@ #include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" #include "phasar/DataFlow/IfdsIde/InitialSeeds.h" #include "phasar/DataFlow/IfdsIde/SolverResults.h" -#include "phasar/PhasarLLVM/Utils/NullAnalysisPrinter.h" #include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/NullAnalysisPrinter.h" #include "phasar/Utils/Printer.h" #include "phasar/Utils/Soundness.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h index 19bdd525a..be868e560 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h @@ -19,6 +19,7 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" @@ -529,10 +530,9 @@ class IDETypeStateAnalysis if (const auto *Alloca = llvm::dyn_cast(Res.first)) { if (Res.second == TSD->error()) { - Warning> - Warn(&I, Res.first, TSD->error()); // ERROR STATE DETECTED - this->Printer->onResult(Warn); + this->Printer->onResult(&I, Res.first, TSD->error(), + TSD->analysisType()); } } } @@ -541,10 +541,9 @@ class IDETypeStateAnalysis if (const auto *Alloca = llvm::dyn_cast(Res.first)) { if (Res.second == TSD->error()) { - Warning> - Warn(&I, Res.first, TSD->error()); // ERROR STATE DETECTED - this->Printer->onResult(Warn); + this->Printer->onResult(&I, Res.first, TSD->error(), + TSD->analysisType()); } } } @@ -553,7 +552,7 @@ class IDETypeStateAnalysis } } - this->Printer->onFinalize(OS); + this->Printer->onFinalize(); } private: diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h index 1f28206d1..c7cad7975 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h @@ -72,6 +72,7 @@ class CSTDFILEIOTypeStateDescription [[nodiscard]] TypeStateDescription::State uninit() const override; [[nodiscard]] TypeStateDescription::State start() const override; [[nodiscard]] TypeStateDescription::State error() const override; + [[nodiscard]] DataFlowAnalysisType analysisType() const override; }; extern template class IDETypeStateAnalysis; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h index 081b94a46..144d3571c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h @@ -102,6 +102,7 @@ class OpenSSLEVPKDFCTXDescription [[nodiscard]] State uninit() const override; [[nodiscard]] State start() const override; [[nodiscard]] State error() const override; + [[nodiscard]] DataFlowAnalysisType analysisType() const override; /* /// Checks all callSites, where a EVP_KDF object needs to be in a /// certain state, such that the state transition for EVP_KDF_CTX is valid. diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h index 34023c863..324333c62 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h @@ -85,6 +85,8 @@ class OpenSSLEVPKDFDescription [[nodiscard]] TypeStateDescription::State start() const override; [[nodiscard]] TypeStateDescription::State error() const override; + + [[nodiscard]] DataFlowAnalysisType analysisType() const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h index cb69f4fe7..597fa2795 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h @@ -72,6 +72,7 @@ class OpenSSLSecureHeapDescription [[nodiscard]] TypeStateDescription::State uninit() const override; [[nodiscard]] TypeStateDescription::State start() const override; [[nodiscard]] TypeStateDescription::State error() const override; + [[nodiscard]] DataFlowAnalysisType analysisType() const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h index a1c41d1c2..57c41710c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h @@ -41,6 +41,7 @@ class OpenSSLSecureMemoryDescription [[nodiscard]] TypeStateDescription::State uninit() const override; [[nodiscard]] TypeStateDescription::State start() const override; [[nodiscard]] TypeStateDescription::State error() const override; + [[nodiscard]] DataFlowAnalysisType analysisType() const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h index 883b1d89d..a7e14e8fc 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h @@ -10,6 +10,8 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_TYPESTATEDESCRIPTION_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_TYPESTATEDESCRIPTION_H +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" + #include "llvm/IR/InstrTypes.h" #include @@ -28,6 +30,7 @@ struct TypeStateDescriptionBase { getConsumerParamIdx(llvm::StringRef F) const = 0; [[nodiscard]] virtual std::set getFactoryParamIdx(llvm::StringRef F) const = 0; + [[nodiscard]] virtual DataFlowAnalysisType analysisType() const = 0; }; /** diff --git a/include/phasar/PhasarLLVM/Utils/AnalysisPrinterBase.h b/include/phasar/PhasarLLVM/Utils/AnalysisPrinterBase.h deleted file mode 100644 index 9190ead07..000000000 --- a/include/phasar/PhasarLLVM/Utils/AnalysisPrinterBase.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef PHASAR_PHASARLLVM_UTILS_ANALYSISPRINTERBASE_H -#define PHASAR_PHASARLLVM_UTILS_ANALYSISPRINTERBASE_H - -#include "llvm/Support/raw_ostream.h" - -namespace psr { - -template struct Warning { - using n_t = typename AnalysisDomainTy::n_t; - using d_t = typename AnalysisDomainTy::d_t; - using l_t = typename AnalysisDomainTy::l_t; - - n_t Instr; - d_t Fact; - l_t LatticeElement; - - // Constructor - Warning(n_t Inst, d_t DfFact, l_t Lattice) - : Instr(std::move(Inst)), Fact(std::move(DfFact)), - LatticeElement(std::move(Lattice)) {} -}; - -template struct DataflowAnalysisResults { - std::vector> War; -}; - -template class AnalysisPrinterBase { -public: - virtual void onResult(Warning /*War*/) = 0; - virtual void onInitialize() = 0; - virtual void onFinalize(llvm::raw_ostream & /*OS*/) const = 0; - - AnalysisPrinterBase() = default; - virtual ~AnalysisPrinterBase() = default; - AnalysisPrinterBase(const AnalysisPrinterBase &) = delete; - AnalysisPrinterBase &operator=(const AnalysisPrinterBase &) = delete; - - AnalysisPrinterBase(AnalysisPrinterBase &&) = delete; - AnalysisPrinterBase &operator=(AnalysisPrinterBase &&) = delete; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/Utils/DefaultAnalysisPrinter.h b/include/phasar/PhasarLLVM/Utils/DefaultAnalysisPrinter.h deleted file mode 100644 index 7cbdf476b..000000000 --- a/include/phasar/PhasarLLVM/Utils/DefaultAnalysisPrinter.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef PHASAR_PHASARLLVM_UTILS_DEFAULTANALYSISPRINTER_H -#define PHASAR_PHASARLLVM_UTILS_DEFAULTANALYSISPRINTER_H - -#include "phasar/Domain/BinaryDomain.h" -#include "phasar/PhasarLLVM/Utils/AnalysisPrinterBase.h" -#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" -#include "phasar/Utils/Printer.h" - -#include -#include -#include - -namespace psr { - -template -class DefaultAnalysisPrinter : public AnalysisPrinterBase { - using l_t = typename AnalysisDomainTy::l_t; - -public: - ~DefaultAnalysisPrinter() override = default; - DefaultAnalysisPrinter() = default; - - void onResult(Warning War) override { - AnalysisResults.War.emplace_back(std::move(War)); - } - - void onInitialize() override{}; - void onFinalize(llvm::raw_ostream &OS = llvm::outs()) const override { - for (const auto &Iter : AnalysisResults.War) { - - OS << "\nAt IR statement: " << NToString(Iter.Instr) << "\n"; - - OS << "\tFact: " << DToString(Iter.Fact) << "\n"; - - if constexpr (std::is_same_v) { - OS << "Value: " << LToString(Iter.LatticeElement) << "\n"; - } - } - } - -private: - DataflowAnalysisResults AnalysisResults{}; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h b/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h index 17cab3ef9..9432e9c92 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h @@ -19,6 +19,7 @@ #include "nlohmann/json.hpp" +#include #include // Forward declaration of types for which we only use its pointer or ref type @@ -39,6 +40,7 @@ namespace psr { [[nodiscard]] std::string getFunctionNameFromIR(const llvm::Value *V); [[nodiscard]] std::string getFilePathFromIR(const llvm::Value *V); +[[nodiscard]] std::string getFilePathFromIR(const llvm::DIFile *DIF); [[nodiscard]] std::string getDirectoryFromIR(const llvm::Value *V); @@ -85,6 +87,15 @@ void to_json(nlohmann::json &J, const SourceCodeInfo &Info); [[nodiscard]] SourceCodeInfo getSrcCodeInfoFromIR(const llvm::Value *V); +struct DebugLocation { + unsigned Line{}; + unsigned Column{}; + const llvm::DIFile *File{}; +}; + +[[nodiscard]] std::optional +getDebugLocation(const llvm::Value *V); + } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/Utils/LLVMSourceManager.h b/include/phasar/PhasarLLVM/Utils/LLVMSourceManager.h new file mode 100644 index 000000000..1828c6e53 --- /dev/null +++ b/include/phasar/PhasarLLVM/Utils/LLVMSourceManager.h @@ -0,0 +1,36 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_LLVMSOURCEMANAGER_H +#define PHASAR_PHASARLLVM_UTILS_LLVMSOURCEMANAGER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/SourceMgr.h" + +#include + +namespace llvm { +class Value; +class DIFile; +} // namespace llvm + +namespace psr { +struct ManagedDebugLocation { + unsigned Line{}; + unsigned Column{}; + unsigned File{}; +}; + +class LLVMSourceManager { +public: + [[nodiscard]] std::optional + getDebugLocation(const llvm::Value *V); + + void print(llvm::raw_ostream &OS, ManagedDebugLocation Loc, + llvm::SourceMgr::DiagKind DiagKind, const llvm::Twine &Message); + +private: + llvm::DenseMap FileIdMap{}; + llvm::SourceMgr SrcMgr{}; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_UTILS_LLVMSOURCEMANAGER_H diff --git a/include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h b/include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h new file mode 100644 index 000000000..58b00e9a9 --- /dev/null +++ b/include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h @@ -0,0 +1,71 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_SOURCEMGRPRINTER_H +#define PHASAR_PHASARLLVM_UTILS_SOURCEMGRPRINTER_H + +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" +#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" +#include "phasar/PhasarLLVM/Utils/LLVMSourceManager.h" +#include "phasar/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/MaybeUniquePtr.h" + +#include "llvm/ADT/FunctionExtras.h" +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { + +namespace detail { +class SourceMgrPrinterBase { +public: + explicit SourceMgrPrinterBase( + llvm::unique_function &&PrintMessage, + llvm::raw_ostream &OS = llvm::errs(), + llvm::SourceMgr::DiagKind WKind = llvm::SourceMgr::DK_Warning); + + explicit SourceMgrPrinterBase( + llvm::unique_function &&PrintMessage, + const llvm::Twine &OutFileName, + llvm::SourceMgr::DiagKind WKind = llvm::SourceMgr::DK_Warning); + +protected: + LLVMSourceManager SrcMgr; + + llvm::unique_function GetPrintMessage; + MaybeUniquePtr OS = &llvm::errs(); + llvm::SourceMgr::DiagKind WarningKind; +}; +} // namespace detail + +template +class SourceMgrPrinter : public AnalysisPrinterBase, + private detail::SourceMgrPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + explicit SourceMgrPrinter( + llvm::unique_function &&PrintMessage, + llvm::raw_ostream &OS = llvm::errs(), + llvm::SourceMgr::DiagKind WKind = llvm::SourceMgr::DK_Warning) + : detail::SourceMgrPrinterBase(std::move(PrintMessage), OS, WKind) {} + +private: + void doOnResult(n_t Inst, d_t Fact, l_t /*Value*/, + DataFlowAnalysisType AnalysisType) override { + auto SrcLoc = SrcMgr.getDebugLocation(Inst); + if constexpr (std::is_convertible_v) { + if (!SrcLoc) { + SrcLoc = + SrcMgr.getDebugLocation(static_cast(Fact)); + } + } + + if (SrcLoc) { + SrcMgr.print(*OS, *SrcLoc, WarningKind, GetPrintMessage(AnalysisType)); + } + } +}; + +} // namespace psr +#endif diff --git a/include/phasar/Utils/AnalysisPrinterBase.h b/include/phasar/Utils/AnalysisPrinterBase.h new file mode 100644 index 000000000..a1ca6aee3 --- /dev/null +++ b/include/phasar/Utils/AnalysisPrinterBase.h @@ -0,0 +1,54 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_ANALYSISPRINTERBASE_H +#define PHASAR_PHASARLLVM_UTILS_ANALYSISPRINTERBASE_H + +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" + +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { + +template class AnalysisPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + template , psr::BinaryDomain>>> + void onResult(n_t Instr, d_t DfFact, l_t LatticeElement, + DataFlowAnalysisType AnalysisType) { + doOnResult(Instr, DfFact, LatticeElement, AnalysisType); + } + + template , psr::BinaryDomain>>> + void onResult(n_t Instr, d_t DfFact, DataFlowAnalysisType AnalysisType) { + doOnResult(Instr, DfFact, psr::BinaryDomain::BOTTOM, AnalysisType); + } + + void onInitialize() { doOnInitialize(); } + + void onFinalize() { doOnFinalize(); } + + AnalysisPrinterBase() = default; + virtual ~AnalysisPrinterBase() = default; + AnalysisPrinterBase(const AnalysisPrinterBase &) = delete; + AnalysisPrinterBase &operator=(const AnalysisPrinterBase &) = delete; + + AnalysisPrinterBase(AnalysisPrinterBase &&) = delete; + AnalysisPrinterBase &operator=(AnalysisPrinterBase &&) = delete; + +private: + virtual void doOnResult(n_t /*Instr*/, d_t /*DfFact*/, l_t /*LatticeElement*/, + DataFlowAnalysisType /*AnalysisType*/) = 0; + + virtual void doOnInitialize() {} + virtual void doOnFinalize() {} +}; + +} // namespace psr + +#endif diff --git a/include/phasar/Utils/DefaultAnalysisPrinter.h b/include/phasar/Utils/DefaultAnalysisPrinter.h new file mode 100644 index 000000000..f9008b946 --- /dev/null +++ b/include/phasar/Utils/DefaultAnalysisPrinter.h @@ -0,0 +1,73 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_DEFAULTANALYSISPRINTER_H +#define PHASAR_PHASARLLVM_UTILS_DEFAULTANALYSISPRINTER_H + +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/IO.h" +#include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/Printer.h" + +#include +#include +#include + +namespace psr { + +template struct Warning { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + + n_t Instr; + d_t Fact; + l_t LatticeElement; + DataFlowAnalysisType AnalysisType; + + // Constructor + Warning(n_t Inst, d_t DfFact, l_t Lattice, + DataFlowAnalysisType DfAnalysisType) + : Instr(std::move(Inst)), Fact(std::move(DfFact)), + LatticeElement(std::move(Lattice)), AnalysisType(DfAnalysisType) {} +}; + +template +class DefaultAnalysisPrinter : public AnalysisPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + explicit DefaultAnalysisPrinter(llvm::raw_ostream &OS = llvm::outs()) + : OS(&OS) {} + + explicit DefaultAnalysisPrinter(const llvm::Twine &Filename) + : AnalysisPrinterBase(), OS(openFileStream(Filename)){}; + + ~DefaultAnalysisPrinter() override = default; + +private: + void doOnResult(n_t Instr, d_t DfFact, l_t Lattice, + DataFlowAnalysisType AnalysisType) override { + AnalysisResults.emplace_back(Instr, DfFact, Lattice, AnalysisType); + } + + void doOnFinalize() override { + for (const auto &Iter : AnalysisResults) { + + *OS << "\nAt IR statement: " << NToString(Iter.Instr) << "\n"; + + *OS << "\tFact: " << DToString(Iter.Fact) << "\n"; + + if constexpr (!std::is_same_v) { + *OS << "Value: " << LToString(Iter.LatticeElement) << "\n"; + } + } + } + + std::vector> AnalysisResults{}; + MaybeUniquePtr OS = &llvm::outs(); +}; + +} // namespace psr + +#endif diff --git a/include/phasar/PhasarLLVM/Utils/NullAnalysisPrinter.h b/include/phasar/Utils/NullAnalysisPrinter.h similarity index 58% rename from include/phasar/PhasarLLVM/Utils/NullAnalysisPrinter.h rename to include/phasar/Utils/NullAnalysisPrinter.h index 900767755..8527cc3c8 100644 --- a/include/phasar/PhasarLLVM/Utils/NullAnalysisPrinter.h +++ b/include/phasar/Utils/NullAnalysisPrinter.h @@ -1,23 +1,26 @@ #ifndef PHASAR_PHASARLLVM_UTILS_NULLANALYSISPRINTER_H #define PHASAR_PHASARLLVM_UTILS_NULLANALYSISPRINTER_H -#include "phasar/PhasarLLVM/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/AnalysisPrinterBase.h" namespace psr { template class NullAnalysisPrinter final : public AnalysisPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + public: static NullAnalysisPrinter *getInstance() { static auto Instance = NullAnalysisPrinter(); return &Instance; } - void onInitialize() override{}; - void onResult(Warning /*War*/) override{}; - void onFinalize(llvm::raw_ostream & /*OS*/) const override{}; - private: + void doOnResult(n_t /*Instr*/, d_t /*DfFact*/, l_t /*Lattice*/, + DataFlowAnalysisType /*AnalysisType*/) override{}; + NullAnalysisPrinter() = default; }; diff --git a/include/phasar/Utils/OnTheFlyAnalysisPrinter.h b/include/phasar/Utils/OnTheFlyAnalysisPrinter.h new file mode 100644 index 000000000..aa3e4ef23 --- /dev/null +++ b/include/phasar/Utils/OnTheFlyAnalysisPrinter.h @@ -0,0 +1,49 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_ONTHEFLYANALYSISPRINTER_H +#define PHASAR_PHASARLLVM_UTILS_ONTHEFLYANALYSISPRINTER_H + +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/IO.h" +#include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/Printer.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" + +#include +namespace psr { + +template +class OnTheFlyAnalysisPrinter : public AnalysisPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + explicit OnTheFlyAnalysisPrinter(llvm::raw_ostream &OS) + : AnalysisPrinterBase(), OS(&OS){}; + + explicit OnTheFlyAnalysisPrinter(const llvm::Twine &Filename) + : AnalysisPrinterBase(), OS(openFileStream(Filename)){}; + + OnTheFlyAnalysisPrinter() = default; + ~OnTheFlyAnalysisPrinter() = default; + + [[nodiscard]] bool isValid() const noexcept { return OS != nullptr; } + +private: + void doOnResult(n_t Instr, d_t DfFact, l_t LatticeElement, + DataFlowAnalysisType /*AnalysisType*/) override { + assert(isValid()); + *OS << "\nAt IR statement: " << NToString(Instr) << "\n"; + *OS << "\tFact: " << DToString(DfFact) << "\n"; + if constexpr (!std::is_same_v) { + *OS << "Value: " << LToString(LatticeElement) << "\n"; + } + } + + MaybeUniquePtr OS = nullptr; +}; +} // namespace psr + +#endif diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.cpp index 6759ed4b9..f5b799d90 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.cpp @@ -19,6 +19,7 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/TransferEdgeFunction.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Pointer/PointsToInfo.h" #include "phasar/Utils/DebugOutput.h" @@ -227,9 +228,8 @@ void IDEExtendedTaintAnalysis::reportLeakIfNecessary( const llvm::Value *LeakCandidate) { if (isSink(SinkCandidate, Inst)) { Leaks[Inst].insert(LeakCandidate); - Warning Warn( - Inst, makeFlowFact(LeakCandidate), Top{}); - Printer->onResult(Warn); + Printer->onResult(Inst, makeFlowFact(LeakCandidate), Top{}, + DataFlowAnalysisType::IDEExtendedTaintAnalysis); } } @@ -754,13 +754,12 @@ void IDEExtendedTaintAnalysis::emitTextReport( for (auto &[Inst, LeakSet] : Leaks) { for (const auto &Leak : LeakSet) { - Warning Warn(Inst, makeFlowFact(Leak), - Top{}); - Printer->onResult(Warn); + Printer->onResult(Inst, makeFlowFact(Leak), Top{}, + DataFlowAnalysisType::IDEExtendedTaintAnalysis); } } - Printer->onFinalize(OS); + Printer->onFinalize(); } // Helpers: diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp index a6ee4458b..a39fbee4b 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp @@ -19,6 +19,7 @@ #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" #include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Logger.h" @@ -410,9 +411,8 @@ auto IFDSTaintAnalysis::getSummaryFlowFunction([[maybe_unused]] n_t CallSite, CallSite](d_t Source) -> container_type { if (Leak.count(Source)) { if (Leaks[CallSite].insert(Source).second) { - Warning Warn(CallSite, Source, - topElement()); - Printer->onResult(Warn); + Printer->onResult(CallSite, Source, + DataFlowAnalysisType::IFDSTaintAnalysis); } } @@ -439,9 +439,8 @@ auto IFDSTaintAnalysis::getSummaryFlowFunction([[maybe_unused]] n_t CallSite, if (Leak.count(Source)) { if (Leaks[CallSite].insert(Source).second) { - Warning Warn(CallSite, Source, - topElement()); - Printer->onResult(Warn); + Printer->onResult(CallSite, Source, + DataFlowAnalysisType::IFDSTaintAnalysis); } } @@ -487,7 +486,7 @@ void IFDSTaintAnalysis::emitTextReport( const SolverResults & /*SR*/, llvm::raw_ostream &OS) { OS << "\n----- Found the following leaks -----\n"; - Printer->onFinalize(OS); + Printer->onFinalize(); } } // namespace psr diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.cpp index 7dea9e963..68e360b35 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.cpp @@ -13,6 +13,7 @@ #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" #include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Logger.h" @@ -188,38 +189,29 @@ IFDSUninitializedVariables::getNormalFlowFunction( }); } // check if some instruction is using an undefined value (in)directly - struct UVFF : FlowFunction { - const llvm::Instruction *Inst; - std::map> &UndefValueUses; - UVFF(const llvm::Instruction *Inst, - std::map> &UVU) - : Inst(Inst), UndefValueUses(UVU) {} - std::set - computeTargets(IFDSUninitializedVariables::d_t Source) override { - for (const auto &Operand : Inst->operands()) { - const llvm::UndefValue *Undef = - llvm::dyn_cast(Operand); - if (Operand == Source || Operand == Undef) { - //---------------------------------------------------------------- - // It is not necessary and (from my point of view) not intended to - // report a leak on EVERY kind of instruction. - // For some of them (e.g. gep, bitcast, ...) propagating the dataflow - // facts may be enough - //---------------------------------------------------------------- - if (!llvm::isa(Inst) && - !llvm::isa(Inst) && - !llvm::isa(Inst)) { - UndefValueUses[Inst].insert(Operand); - } - return {Source, Inst}; + + return lambdaFlow([Curr, this](d_t Source) -> std::set { + for (const auto &Operand : Curr->operands()) { + const llvm::UndefValue *Undef = llvm::dyn_cast(Operand); + if (Operand == Source || Operand == Undef) { + //---------------------------------------------------------------- + // It is not necessary and (from my point of view) not intended to + // report a leak on EVERY kind of instruction. + // For some of them (e.g. gep, bitcast, ...) propagating the dataflow + // facts may be enough + //---------------------------------------------------------------- + if (!llvm::isa(Curr) && + !llvm::isa(Curr) && + !llvm::isa(Curr)) { + UndefValueUses[Curr].insert(Operand); + Printer->onResult(Curr, Operand, + DataFlowAnalysisType::IFDSUninitializedVariables); } + return {Source, Curr}; } - return {Source}; } - }; - return std::make_shared(Curr, UndefValueUses); + return {Source}; + }); } IFDSUninitializedVariables::FlowFunctionPtrType @@ -441,6 +433,7 @@ void IFDSUninitializedVariables::emitTextReport( Res.print(OS); } } + Printer->onFinalize(); } } diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.cpp index ba640d0ba..018ac4d93 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.cpp @@ -192,6 +192,11 @@ CSTDFILEIOState CSTDFILEIOTypeStateDescription::error() const { return CSTDFILEIOState::ERROR; } +[[nodiscard]] DataFlowAnalysisType +CSTDFILEIOTypeStateDescription::analysisType() const { + return DataFlowAnalysisType::IDECSTDIOTypeStateAnalysis; +} + template class IDETypeStateAnalysis; } // namespace psr diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.cpp index df37f0bcb..38a4e22af 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.cpp @@ -194,6 +194,11 @@ OpenSSLEVPKDFCTXState OpenSSLEVPKDFCTXDescription::error() const { return OpenSSLEVPKDFCTXState::ERROR; } +[[nodiscard]] DataFlowAnalysisType +OpenSSLEVPKDFCTXDescription::analysisType() const { + return DataFlowAnalysisType::IDEOpenSSLTypeStateAnalysis; +} + OpenSSLEVPKDFCTXDescription::OpenSSLEVTKDFToken OpenSSLEVPKDFCTXDescription::funcNameToToken(llvm::StringRef F) { if (F == "EVP_KDF_CTX_new") { diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.cpp index 4c0973cf7..d881af8ad 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.cpp @@ -130,6 +130,11 @@ OpenSSLEVPKDFState OpenSSLEVPKDFDescription::error() const { return OpenSSLEVPKDFState::ERROR; } +[[nodiscard]] DataFlowAnalysisType +OpenSSLEVPKDFDescription::analysisType() const { + return DataFlowAnalysisType::IDEOpenSSLTypeStateAnalysis; +} + OpenSSLEVPKDFDescription::OpenSSLEVTKDFToken OpenSSLEVPKDFDescription::funcNameToToken(llvm::StringRef FuncName) { if (FuncName == "EVP_KDF_fetch") { diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.cpp index 3c0a90a67..716f682a4 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.cpp @@ -168,6 +168,11 @@ OpenSSLSecureHeapState OpenSSLSecureHeapDescription::error() const { return OpenSSLSecureHeapState::ERROR; } +[[nodiscard]] DataFlowAnalysisType +OpenSSLSecureHeapDescription::analysisType() const { + return DataFlowAnalysisType::IDEOpenSSLTypeStateAnalysis; +} + OpenSSLSecureHeapDescription::OpenSSLSecureHeapToken OpenSSLSecureHeapDescription::funcNameToToken(llvm::StringRef F) { return llvm::StringSwitch(F) diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.cpp index 04d8eba6e..a67f17e42 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.cpp @@ -170,4 +170,9 @@ OpenSSLSecureMemoryState OpenSSLSecureMemoryDescription::error() const { return OpenSSLSecureMemoryState::ERROR; } +[[nodiscard]] DataFlowAnalysisType +OpenSSLSecureMemoryDescription::analysisType() const { + return DataFlowAnalysisType::IDEOpenSSLTypeStateAnalysis; +} + } // namespace psr diff --git a/lib/PhasarLLVM/Utils/LLVMIRToSrc.cpp b/lib/PhasarLLVM/Utils/LLVMIRToSrc.cpp index d32c9a3ad..01dd07603 100644 --- a/lib/PhasarLLVM/Utils/LLVMIRToSrc.cpp +++ b/lib/PhasarLLVM/Utils/LLVMIRToSrc.cpp @@ -9,7 +9,7 @@ #include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" -#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" #include "llvm/IR/DebugInfoMetadata.h" @@ -20,19 +20,17 @@ #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/Value.h" - -#include "boost/algorithm/string/trim.hpp" +#include "llvm/Support/Path.h" #include #include #include +#include #include using namespace psr; -namespace psr { - -llvm::DbgVariableIntrinsic *getDbgVarIntrinsic(const llvm::Value *V) { +static llvm::DbgVariableIntrinsic *getDbgVarIntrinsic(const llvm::Value *V) { if (auto *VAM = llvm::ValueAsMetadata::getIfExists( const_cast(V))) { // NOLINT FIXME when LLVM supports it if (auto *MDV = llvm::MetadataAsValue::getIfExists(V->getContext(), VAM)) { @@ -58,7 +56,7 @@ llvm::DbgVariableIntrinsic *getDbgVarIntrinsic(const llvm::Value *V) { return nullptr; } -llvm::DILocalVariable *getDILocalVariable(const llvm::Value *V) { +static llvm::DILocalVariable *getDILocalVariable(const llvm::Value *V) { if (auto *DbgIntr = getDbgVarIntrinsic(V)) { if (auto *DDI = llvm::dyn_cast(DbgIntr)) { return DDI->getVariable(); @@ -70,7 +68,7 @@ llvm::DILocalVariable *getDILocalVariable(const llvm::Value *V) { return nullptr; } -llvm::DIGlobalVariable *getDIGlobalVariable(const llvm::Value *V) { +static llvm::DIGlobalVariable *getDIGlobalVariable(const llvm::Value *V) { if (const auto *GV = llvm::dyn_cast(V)) { if (auto *MN = GV->getMetadata(llvm::LLVMContext::MD_dbg)) { if (auto *DIGVExp = @@ -82,14 +80,14 @@ llvm::DIGlobalVariable *getDIGlobalVariable(const llvm::Value *V) { return nullptr; } -llvm::DISubprogram *getDISubprogram(const llvm::Value *V) { +static llvm::DISubprogram *getDISubprogram(const llvm::Value *V) { if (const auto *F = llvm::dyn_cast(V)) { return F->getSubprogram(); } return nullptr; } -llvm::DILocation *getDILocation(const llvm::Value *V) { +static llvm::DILocation *getDILocation(const llvm::Value *V) { // Arguments and Instruction such as AllocaInst if (auto *DbgIntr = getDbgVarIntrinsic(V)) { if (auto *MN = DbgIntr->getMetadata(llvm::LLVMContext::MD_dbg)) { @@ -103,7 +101,7 @@ llvm::DILocation *getDILocation(const llvm::Value *V) { return nullptr; } -std::string getVarNameFromIR(const llvm::Value *V) { +std::string psr::getVarNameFromIR(const llvm::Value *V) { if (auto *LocVar = getDILocalVariable(V)) { return LocVar->getName().str(); } @@ -113,7 +111,7 @@ std::string getVarNameFromIR(const llvm::Value *V) { return ""; } -std::string getFunctionNameFromIR(const llvm::Value *V) { +std::string psr::getFunctionNameFromIR(const llvm::Value *V) { // We can return unmangled function names w/o checking debug info if (const auto *F = llvm::dyn_cast(V)) { return F->getName().str(); @@ -127,35 +125,47 @@ std::string getFunctionNameFromIR(const llvm::Value *V) { return ""; } -std::string getFilePathFromIR(const llvm::Value *V) { +std::string psr::getFilePathFromIR(const llvm::Value *V) { if (const auto *DIF = getDIFileFromIR(V)) { - std::filesystem::path File(DIF->getFilename().str()); - std::filesystem::path Dir(DIF->getDirectory().str()); - if (!File.empty()) { - // try to concatenate file path and dir to get absolut path - if (!File.has_root_directory() && !Dir.empty()) { - File = Dir / File; - } - return File.string(); - } - } else { - /* As a fallback solution, we will return 'source_filename' info from - * module. However, it is not guaranteed to contain the absoult path, and it - * will return 'llvm-link' for linked modules. */ - if (const auto *F = llvm::dyn_cast(V)) { - return F->getParent()->getSourceFileName(); - } - if (const auto *Arg = llvm::dyn_cast(V)) { - return Arg->getParent()->getParent()->getSourceFileName(); - } - if (const auto *I = llvm::dyn_cast(V)) { - return I->getFunction()->getParent()->getSourceFileName(); - } + return getFilePathFromIR(DIF); } - return ""; + /* As a fallback solution, we will return 'source_filename' info from + * module. However, it is not guaranteed to contain the absoult path, and it + * will return 'llvm-link' for linked modules. */ + if (const auto *F = llvm::dyn_cast(V)) { + return F->getParent()->getSourceFileName(); + } + if (const auto *Arg = llvm::dyn_cast(V)) { + return Arg->getParent()->getParent()->getSourceFileName(); + } + if (const auto *I = llvm::dyn_cast(V)) { + return I->getFunction()->getParent()->getSourceFileName(); + } + + return {}; +} + +std::string psr::getFilePathFromIR(const llvm::DIFile *DIF) { + auto FileName = DIF->getFilename(); + auto DirName = DIF->getDirectory(); + + if (FileName.empty()) { + return {}; + } + + // try to concatenate file path and dir to get absolute path + if (!DirName.empty() && + !llvm::sys::path::has_root_directory(DIF->getFilename())) { + llvm::SmallString<256> Buf; + llvm::sys::path::append(Buf, DirName, FileName); + + return Buf.str().str(); + } + + return FileName.str(); } -const llvm::DIFile *getDIFileFromIR(const llvm::Value *V) { +const llvm::DIFile *psr::getDIFileFromIR(const llvm::Value *V) { if (const auto *GO = llvm::dyn_cast(V)) { if (auto *MN = GO->getMetadata(llvm::LLVMContext::MD_dbg)) { if (auto *Subpr = llvm::dyn_cast(MN)) { @@ -181,7 +191,7 @@ const llvm::DIFile *getDIFileFromIR(const llvm::Value *V) { return nullptr; } -std::string getDirectoryFromIR(const llvm::Value *V) { +std::string psr::getDirectoryFromIR(const llvm::Value *V) { // Argument and Instruction if (auto *DILoc = getDILocation(V)) { return DILoc->getDirectory().str(); @@ -195,7 +205,7 @@ std::string getDirectoryFromIR(const llvm::Value *V) { return ""; } -unsigned int getLineFromIR(const llvm::Value *V) { +unsigned int psr::getLineFromIR(const llvm::Value *V) { // Argument and Instruction if (auto *DILoc = getDILocation(V)) { return DILoc->getLine(); @@ -209,7 +219,7 @@ unsigned int getLineFromIR(const llvm::Value *V) { return 0; } -unsigned int getColumnFromIR(const llvm::Value *V) { +unsigned int psr::getColumnFromIR(const llvm::Value *V) { // Globals and Function have no column info if (auto *DILoc = getDILocation(V)) { return DILoc->getColumn(); @@ -217,7 +227,7 @@ unsigned int getColumnFromIR(const llvm::Value *V) { return 0; } -std::pair getLineAndColFromIR(const llvm::Value *V) { +std::pair psr::getLineAndColFromIR(const llvm::Value *V) { // Argument and Instruction if (auto *DILoc = getDILocation(V)) { return {DILoc->getLine(), DILoc->getColumn()}; @@ -231,7 +241,7 @@ std::pair getLineAndColFromIR(const llvm::Value *V) { return {0, 0}; } -std::string getSrcCodeFromIR(const llvm::Value *V, bool Trim) { +std::string psr::getSrcCodeFromIR(const llvm::Value *V, bool Trim) { unsigned int LineNr = getLineFromIR(V); if (LineNr > 0) { std::filesystem::path Path(getFilePathFromIR(V)); @@ -251,7 +261,7 @@ std::string getSrcCodeFromIR(const llvm::Value *V, bool Trim) { return ""; } -std::string getModuleIDFromIR(const llvm::Value *V) { +std::string psr::getModuleIDFromIR(const llvm::Value *V) { if (const auto *GO = llvm::dyn_cast(V)) { return GO->getParent()->getModuleIdentifier(); } @@ -294,7 +304,7 @@ bool SourceCodeInfo::equivalentWith(const SourceCodeInfo &Other) const { .slice(Pos + 1, llvm::StringRef::npos)); } -void from_json(const nlohmann::json &J, SourceCodeInfo &Info) { +void psr::from_json(const nlohmann::json &J, SourceCodeInfo &Info) { J.at("sourceCodeLine").get_to(Info.SourceCodeLine); J.at("sourceCodeFileName").get_to(Info.SourceCodeFilename); if (auto Fn = J.find("sourceCode"); Fn != J.end()) { @@ -303,7 +313,7 @@ void from_json(const nlohmann::json &J, SourceCodeInfo &Info) { J.at("line").get_to(Info.Line); J.at("column").get_to(Info.Column); } -void to_json(nlohmann::json &J, const SourceCodeInfo &Info) { +void psr::to_json(nlohmann::json &J, const SourceCodeInfo &Info) { J = nlohmann::json{ {"sourceCodeLine", Info.SourceCodeLine}, {"sourceCodeFileName", Info.SourceCodeFilename}, @@ -313,7 +323,7 @@ void to_json(nlohmann::json &J, const SourceCodeInfo &Info) { }; } -SourceCodeInfo getSrcCodeInfoFromIR(const llvm::Value *V) { +SourceCodeInfo psr::getSrcCodeInfoFromIR(const llvm::Value *V) { return SourceCodeInfo{ getSrcCodeFromIR(V), getFilePathFromIR(V), @@ -323,4 +333,18 @@ SourceCodeInfo getSrcCodeInfoFromIR(const llvm::Value *V) { }; } -} // namespace psr +std::optional psr::getDebugLocation(const llvm::Value *V) { + // Argument and Instruction + if (auto *DILoc = getDILocation(V)) { + return DebugLocation{DILoc->getLine(), DILoc->getColumn(), + DILoc->getFile()}; + } + if (auto *DISubpr = getDISubprogram(V)) { // Function + return DebugLocation{DISubpr->getLine(), 0, DISubpr->getFile()}; + } + if (auto *DIGV = getDIGlobalVariable(V)) { // Globals + return DebugLocation{DIGV->getLine(), 0, DIGV->getFile()}; + } + + return std::nullopt; +} diff --git a/lib/PhasarLLVM/Utils/LLVMSourceManager.cpp b/lib/PhasarLLVM/Utils/LLVMSourceManager.cpp new file mode 100644 index 000000000..dba8f4809 --- /dev/null +++ b/lib/PhasarLLVM/Utils/LLVMSourceManager.cpp @@ -0,0 +1,54 @@ +#include "phasar/PhasarLLVM/Utils/LLVMSourceManager.h" + +#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" +#include "phasar/Utils/Logger.h" + +#include "llvm/IR/DebugInfoMetadata.h" + +#include + +using namespace psr; + +static std::optional +getSourceBufId(const llvm::DIFile *File, + llvm::DenseMap &FileIdMap, + llvm::SourceMgr &SrcMgr) { + if (auto It = FileIdMap.find(File); It != FileIdMap.end()) { + return It->second; + } + + auto FileName = psr::getFilePathFromIR(File); + auto Buf = llvm::MemoryBuffer::getFile(FileName, true); + if (!Buf) { + PHASAR_LOG_LEVEL(WARNING, "Source File not accessible: " << FileName); + PHASAR_LOG_LEVEL(INFO, "> " << Buf.getError().message()); + return std::nullopt; + } + + auto Id = SrcMgr.AddNewSourceBuffer(std::move(Buf.get()), llvm::SMLoc{}); + FileIdMap.try_emplace(File, Id); + return Id; +} + +std::optional +LLVMSourceManager::getDebugLocation(const llvm::Value *V) { + auto Loc = psr::getDebugLocation(V); + if (!Loc) { + return std::nullopt; + } + + auto BufId = getSourceBufId(Loc->File, FileIdMap, SrcMgr); + if (!BufId) { + return std::nullopt; + } + + return ManagedDebugLocation{Loc->Line, Loc->Column, *BufId}; +} + +void LLVMSourceManager::print(llvm::raw_ostream &OS, ManagedDebugLocation Loc, + llvm::SourceMgr::DiagKind DiagKind, + const llvm::Twine &Message) { + auto SMLoc = SrcMgr.FindLocForLineAndColumn(Loc.File, Loc.Line, Loc.Column); + + SrcMgr.PrintMessage(OS, SMLoc, DiagKind, Message); +} diff --git a/lib/PhasarLLVM/Utils/SourceMgrPrinter.cpp b/lib/PhasarLLVM/Utils/SourceMgrPrinter.cpp new file mode 100644 index 000000000..01687a5dc --- /dev/null +++ b/lib/PhasarLLVM/Utils/SourceMgrPrinter.cpp @@ -0,0 +1,16 @@ +#include "phasar/PhasarLLVM/Utils/SourceMgrPrinter.h" + +#include "phasar/Utils/IO.h" + +using namespace psr; + +detail::SourceMgrPrinterBase::SourceMgrPrinterBase( + llvm::unique_function &&PrintMessage, + llvm::raw_ostream &OS, llvm::SourceMgr::DiagKind WKind) + : GetPrintMessage(std::move(PrintMessage)), OS(&OS), WarningKind(WKind) {} + +detail::SourceMgrPrinterBase::SourceMgrPrinterBase( + llvm::unique_function &&PrintMessage, + const llvm::Twine &OutFileName, llvm::SourceMgr::DiagKind WKind) + : GetPrintMessage(std::move(PrintMessage)), OS(openFileStream(OutFileName)), + WarningKind(WKind) {} diff --git a/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGExportTest.cpp b/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGExportTest.cpp index 9b4b18046..18f503fc3 100644 --- a/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGExportTest.cpp +++ b/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGExportTest.cpp @@ -152,20 +152,27 @@ class LLVMBasedICFGExportTest : public ::testing::Test { ASSERT_TRUE(ExportedICFG.is_array()); EXPECT_EQ(GroundTruth.size(), ExportedICFG.size()); - + bool HasError = false; for (const auto >Json : GroundTruth) { auto GTFrom = GTJson.at("from").get(); auto GTTo = GTJson.at("to").get(); - EXPECT_TRUE(std::any_of(ExportedICFG.begin(), ExportedICFG.end(), - [&](const nlohmann::json &ExportedInfoJson) { - return ExportedInfoJson.at("from") - .get() - .equivalentWith(GTFrom) && - ExportedInfoJson.at("to") - .get() - .equivalentWith(GTTo); - })) - << "No exported equivalent to " << GTJson.dump(4); + + bool HasMatch = std::any_of(ExportedICFG.begin(), ExportedICFG.end(), + [&](const nlohmann::json &ExportedInfoJson) { + return ExportedInfoJson.at("from") + .get() + .equivalentWith(GTFrom) && + ExportedInfoJson.at("to") + .get() + .equivalentWith(GTTo); + }); + + EXPECT_TRUE(HasMatch) << "No exported equivalent to " << GTJson.dump(4); + HasError |= !HasMatch; + } + + if (HasError) { + llvm::errs() << "Errorneous json: " << ExportedICFG.dump(4) << '\n'; } } diff --git a/unittests/Utils/AnalysisPrinterTest.cpp b/unittests/Utils/AnalysisPrinterTest.cpp index 8f1dfb70f..34a110d53 100644 --- a/unittests/Utils/AnalysisPrinterTest.cpp +++ b/unittests/Utils/AnalysisPrinterTest.cpp @@ -7,9 +7,9 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" #include "phasar/PhasarLLVM/SimpleAnalysisConstructor.h" #include "phasar/PhasarLLVM/TaintConfig/TaintConfigData.h" -#include "phasar/PhasarLLVM/Utils/DefaultAnalysisPrinter.h" #include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/DefaultAnalysisPrinter.h" #include "llvm/ADT/DenseMap.h" @@ -22,6 +22,11 @@ using CallBackPairTy = std::pair::config_callback_t, // Use template to variate between Typesate and Taint analysis class GroundTruthCollector : public DefaultAnalysisPrinter { + + using n_t = IDEExtendedTaintAnalysisDomain::n_t; + using d_t = IDEExtendedTaintAnalysisDomain::d_t; + using l_t = IDEExtendedTaintAnalysisDomain::l_t; + public: // constructor init Groundtruth in each fixture GroundTruthCollector(llvm::DenseMap> &GroundTruth) @@ -38,20 +43,19 @@ class GroundTruthCollector } } - void onResult(Warning War) override { +private: + void doOnResult(n_t Instr, d_t DfFact, l_t /*LatticeElement*/, + DataFlowAnalysisType /*AnalysisType*/) override { llvm::DenseMap> FoundLeak; - int SinkId = stoi(getMetaDataID(War.Instr)); + int SinkId = stoi(getMetaDataID(Instr)); std::set LeakedValueIds; - LeakedValueIds.insert(getMetaDataID((War.Fact)->base())); + LeakedValueIds.insert(getMetaDataID((DfFact)->base())); FoundLeak.try_emplace(SinkId, LeakedValueIds); findAndRemove(FoundLeak, GroundTruth); } - void onFinalize(llvm::raw_ostream & /*OS*/ = llvm::outs()) const override { - EXPECT_TRUE(GroundTruth.empty()); - } + void doOnFinalize() override { EXPECT_TRUE(GroundTruth.empty()); } -private: llvm::DenseMap> GroundTruth{}; }; diff --git a/unittests/Utils/CMakeLists.txt b/unittests/Utils/CMakeLists.txt index 62831a3c1..73c042570 100644 --- a/unittests/Utils/CMakeLists.txt +++ b/unittests/Utils/CMakeLists.txt @@ -8,6 +8,8 @@ set(UtilsSources PAMMTest.cpp StableVectorTest.cpp AnalysisPrinterTest.cpp + OnTheFlyAnalysisPrinterTest.cpp + SourceMgrPrinterTest.cpp ) if(PHASAR_ENABLE_DYNAMIC_LOG) diff --git a/unittests/Utils/OnTheFlyAnalysisPrinterTest.cpp b/unittests/Utils/OnTheFlyAnalysisPrinterTest.cpp new file mode 100644 index 000000000..f3cd874d5 --- /dev/null +++ b/unittests/Utils/OnTheFlyAnalysisPrinterTest.cpp @@ -0,0 +1,105 @@ +#include "phasar/Utils/OnTheFlyAnalysisPrinter.h" + +#include "phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h" +#include "phasar/PhasarLLVM/HelperAnalyses.h" +#include "phasar/PhasarLLVM/SimpleAnalysisConstructor.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +using namespace psr; + +class GroundTruthCollector + : public OnTheFlyAnalysisPrinter { + using n_t = LLVMIFDSAnalysisDomainDefault::n_t; + using d_t = LLVMIFDSAnalysisDomainDefault::d_t; + using l_t = LLVMIFDSAnalysisDomainDefault::l_t; + +public: + // constructor init Groundtruth in each fixture + GroundTruthCollector(llvm::DenseMap> &GroundTruth) + : GroundTruth(GroundTruth){}; + + void findAndRemove(int LeakId, const std::string &LeakedFactId) { + + auto It = GroundTruth.find(LeakId); + ASSERT_NE(It, GroundTruth.end()) + << "Found leak at unexpected location: " << LeakId << ": '" + << LeakedFactId << "'"; + + bool Erased = It->second.erase(LeakedFactId); + ASSERT_TRUE(Erased) << "Did not expect leak '" << LeakedFactId + << "' at instruction " << LeakId; + + if (It->second.empty()) { + GroundTruth.erase(It); + } + } + +private: + void doOnResult(n_t Instr, d_t DfFact, l_t /*LatticeElement*/, + DataFlowAnalysisType /*AnalysisType*/) override { + int LeakId = stoi(getMetaDataID(Instr)); + findAndRemove(LeakId, getMetaDataID(DfFact)); + } + + void doOnFinalize() override { EXPECT_TRUE(GroundTruth.empty()); } + + llvm::DenseMap> GroundTruth{}; +}; + +class OnTheFlyAnalysisPrinterTest : public ::testing::Test { +protected: + static constexpr auto PathToLlFiles = + PHASAR_BUILD_SUBFOLDER("uninitialized_variables/"); + + const std::vector EntryPoints = {"main"}; + std::optional UnInitProblem; + std::optional HA; + + void initialize(const llvm::Twine &IRFile) { + HA.emplace(PathToLlFiles + IRFile, EntryPoints); + UnInitProblem = + createAnalysisProblem(*HA, EntryPoints); + } + + void doAnalysisTest(llvm::StringRef IRFile, GroundTruthCollector >Printer) { + initialize(IRFile); + UnInitProblem->setAnalysisPrinter(>Printer); + IFDSSolver Solver(*UnInitProblem, &HA->getICFG()); + Solver.solve(); + GTPrinter.onFinalize(); + } +}; + +/* ============== BASIC TESTS ============== */ + +TEST_F(OnTheFlyAnalysisPrinterTest, UninitTest_01_LEAK) { + + llvm::DenseMap> GroundTruth; + // %4 = load i32, i32* %2, ID: 6 ; %2 is the uninitialized variable i + GroundTruth[6] = {"1"}; + // %5 = add nsw i32 %4, 10 ; %4 is undef, since it is loaded from + // undefined alloca; not sure if it is necessary to report again + GroundTruth[7] = {"6"}; + + GroundTruthCollector GroundTruthPrinter = {GroundTruth}; + doAnalysisTest("binop_uninit_cpp_dbg.ll", GroundTruthPrinter); +} + +TEST_F(OnTheFlyAnalysisPrinterTest, UninitTest_02_NOLEAK) { + llvm::DenseMap> GroundTruth; + GroundTruthCollector GroundTruthPrinter = {GroundTruth}; + doAnalysisTest("ctor_default_cpp_dbg.ll", GroundTruthPrinter); +} + +// main function for the test case +int main(int Argc, char **Argv) { + ::testing::InitGoogleTest(&Argc, Argv); + return RUN_ALL_TESTS(); +} diff --git a/unittests/Utils/SourceMgrPrinterTest.cpp b/unittests/Utils/SourceMgrPrinterTest.cpp new file mode 100644 index 000000000..59f4fc69a --- /dev/null +++ b/unittests/Utils/SourceMgrPrinterTest.cpp @@ -0,0 +1,95 @@ +#include "phasar/PhasarLLVM/Utils/SourceMgrPrinter.h" + +#include "phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h" +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" +#include "phasar/PhasarLLVM/HelperAnalyses.h" +#include "phasar/PhasarLLVM/SimpleAnalysisConstructor.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" +#include "phasar/Utils/AnalysisPrinterBase.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +#include +#include + +using namespace psr; +namespace { +class GroundTruthCollector + : public SourceMgrPrinter { + using n_t = LLVMIFDSAnalysisDomainDefault::n_t; + using d_t = LLVMIFDSAnalysisDomainDefault::d_t; + using l_t = LLVMIFDSAnalysisDomainDefault::l_t; + +public: + // constructor init Groundtruth in each fixture + GroundTruthCollector(std::initializer_list GroundTruth, + bool Dump = false) + : SourceMgrPrinter( + [](DataFlowAnalysisType) { return ""; }, OS), + GroundTruth(GroundTruth), OS(Str), Dump(Dump){}; + +private: + void doOnFinalize() override { + for (auto It = GroundTruth.begin(); It != GroundTruth.end();) { + if (OS.str().find(*It)) { + GroundTruth.erase(It++); + } else { + ++It; + } + } + EXPECT_TRUE(GroundTruth.empty()); + + if (Dump) { + llvm::errs() << Str << '\n'; + } + } + + std::set GroundTruth{}; + std::string Str; + llvm::raw_string_ostream OS; + bool Dump{}; +}; + +class SourceMgrPrinterTest : public ::testing::Test { +protected: + static constexpr auto PathToLlFiles = + PHASAR_BUILD_SUBFOLDER("uninitialized_variables/"); + + const std::vector EntryPoints = {"main"}; + + void doAnalysisTest( + llvm::StringRef IRFile, + AnalysisPrinterBase >Printer) { + HelperAnalyses HA(PathToLlFiles + IRFile, EntryPoints); + auto UnInitProblem = + createAnalysisProblem(HA, EntryPoints); + + UnInitProblem.setAnalysisPrinter(>Printer); + GTPrinter.onInitialize(); + IFDSSolver Solver(UnInitProblem, &HA.getICFG()); + Solver.solve(); + GTPrinter.onFinalize(); + } +}; + +/* ============== BASIC TESTS ============== */ + +TEST_F(SourceMgrPrinterTest, UninitTest_01_LEAK) { + GroundTruthCollector GroundTruthPrinter( + {"/binop_uninit.cpp:3:11:", "/binop_uninit.cpp:3:13:"}); + + doAnalysisTest("binop_uninit_cpp_dbg.ll", GroundTruthPrinter); +} +} // namespace + +// main function for the test case +int main(int Argc, char **Argv) { + ::testing::InitGoogleTest(&Argc, Argv); + return RUN_ALL_TESTS(); +}