diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVCompare.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVCompare.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVCompare.h @@ -0,0 +1,89 @@ +//===-- LVCompare.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the LVCompare class, which is used to describe a logical +// view comparison. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVCOMPARE_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVCOMPARE_H + +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" + +namespace llvm { +namespace logicalview { + +class LVReader; + +// Record the elements missing or added and their compare pass. +using LVPassEntry = std::tuple; +using LVPassTable = std::vector; + +class LVCompare final { + raw_ostream &OS; + LVScopes ScopeStack; + + // As the comparison is performed twice (by exchanging the reference + // and target readers) the element missing/added status does specify + // the comparison pass. + // By recording each missing/added elements along with its pass, it + // allows checking which elements were missing/added during each pass. + LVPassTable PassTable; + + // Reader used on the LHS of the comparison. + // In the 'Missing' pass, it points to the reference reader. + // In the 'Added' pass it points to the target reader. + LVReader *Reader = nullptr; + + bool FirstMissing = true; + bool PrintLines = false; + bool PrintScopes = false; + bool PrintSymbols = false; + bool PrintTypes = false; + + static void setInstance(LVCompare *Compare); + + void printCurrentStack(); + void printSummary() const; + +public: + LVCompare() = delete; + LVCompare(raw_ostream &OS); + LVCompare(const LVCompare &) = delete; + LVCompare &operator=(const LVCompare &) = delete; + ~LVCompare() = default; + + static LVCompare &getInstance(); + + // Scopes stack used during the missing/added reporting. + void push(LVScope *Scope) { ScopeStack.push_back(Scope); } + void pop() { ScopeStack.pop_back(); } + + // Perform comparison between the 'Reference' and 'Target' scopes tree. + Error execute(LVReader *ReferenceReader, LVReader *TargetReader); + + void addPassEntry(LVReader *Reader, LVElement *Element, LVComparePass Pass) { + PassTable.emplace_back(Reader, Element, Pass); + } + const LVPassTable &getPassTable() const & { return PassTable; } + + void printItem(LVElement *Element, LVComparePass Pass); + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +inline LVCompare &getComparator() { return LVCompare::getInstance(); } + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVCOMPARE_H diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h @@ -336,6 +336,14 @@ virtual void resolveReferences() {} void resolveParents(); + bool referenceMatch(const LVElement *Element) const; + + // Returns true if current element is logically equal to the given 'Element'. + bool equals(const LVElement *Element) const; + + // Report the current element as missing or added during comparison. + virtual void report(LVComparePass Pass) {} + static LVElementDispatch &getDispatch() { return Dispatch; } }; diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h @@ -42,6 +42,9 @@ LVProperties Kinds; static LVLineDispatch Dispatch; + // Find the current line in the given 'Targets'. + LVLine *findIn(const LVLines *Targets) const; + public: LVLine() : LVElement(LVSubclassID::LV_LINE) { setIsLine(); @@ -83,6 +86,22 @@ static LVLineDispatch &getDispatch() { return Dispatch; } + // Iterate through the 'References' set and check that all its elements + // are present in the 'Targets' set. For a missing element, mark its + // parents as missing. + static void markMissingParents(const LVLines *References, + const LVLines *Targets); + + // Returns true if current line is logically equal to the given 'Line'. + virtual bool equals(const LVLine *Line) const; + + // Returns true if the given 'References' are logically equal to the + // given 'Targets'. + static bool equals(const LVLines *References, const LVLines *Targets); + + // Report the current line as missing or added during comparison. + void report(LVComparePass Pass) override; + void print(raw_ostream &OS, bool Full = true) const override; void printExtra(raw_ostream &OS, bool Full = true) const override {} @@ -114,6 +133,9 @@ setIsDiscriminator(); } + // Returns true if current line is logically equal to the given 'Line'. + bool equals(const LVLine *Line) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -130,6 +152,9 @@ return std::string(8, ' '); }; + // Returns true if current line is logically equal to the given 'Line'. + bool equals(const LVLine *Line) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h @@ -101,6 +101,7 @@ const LVAddress MaxAddress = std::numeric_limits::max(); enum class LVBinaryType { NONE, ELF, COFF }; +enum class LVComparePass { Missing, Added }; // Validate functions. using LVValidLocation = bool (LVLocation::*)(); @@ -319,6 +320,9 @@ LVObject *Parent, StringRef Value, bool UseQuotes = false, bool PrintRef = false) const; + // Mark branch as missing (current element and parents). + void markBranchAsMissing(); + // Prints the common information for an object (name, type, etc). virtual void print(raw_ostream &OS, bool Full = true) const; // Prints additional information for an object, depending on its kind diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h @@ -61,6 +61,12 @@ using LVCompileUnits = std::map; LVCompileUnits CompileUnits; + // Added elements to be used during elements comparison. + LVLines Lines; + LVScopes Scopes; + LVSymbols Symbols; + LVTypes Types; + // Create split folder. Error createSplitFolder(); bool OutputSplit = false; @@ -140,6 +146,29 @@ // Access to split context. LVSplitContext &getSplitContext() { return SplitContext; } + // In the case of element comparison, register that added element. + void notifyAddedElement(LVLine *Line) { + if (!options().getCompareContext() && options().getCompareLines()) + Lines.push_back(Line); + } + void notifyAddedElement(LVScope *Scope) { + if (!options().getCompareContext() && options().getCompareScopes()) + Scopes.push_back(Scope); + } + void notifyAddedElement(LVSymbol *Symbol) { + if (!options().getCompareContext() && options().getCompareSymbols()) + Symbols.push_back(Symbol); + } + void notifyAddedElement(LVType *Type) { + if (!options().getCompareContext() && options().getCompareTypes()) + Types.push_back(Type); + } + + const LVLines &getLines() const { return Lines; } + const LVScopes &getScopes() const { return Scopes; } + const LVSymbols &getSymbols() const { return Symbols; } + const LVTypes &getTypes() const { return Types; } + // Conditions to print an object. bool doPrintLine(const LVLine *Line) const { return patterns().printElement(Line); diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h @@ -101,6 +101,9 @@ // only-globals, only-locals, a-pattern. bool resolvePrinting() const; + // Find the current scope in the given 'Targets'. + LVScope *findIn(const LVScopes *Targets) const; + // Traverse the scope parent tree, executing the given callback function // on each scope. void traverseParents(LVScopeGetFunction GetFunction, @@ -249,6 +252,10 @@ // DW_AT_specification, DW_AT_abstract_origin, DW_AT_extension. virtual LVScope *getReference() const { return nullptr; } + LVScope *getCompileUnitParent() const override { + return LVElement::getCompileUnitParent(); + } + // Follow a chain of references given by DW_AT_abstract_origin and/or // DW_AT_specification and update the scope name. StringRef resolveReferencesChain(); @@ -268,6 +275,35 @@ void resolveElements(); + // Iterate through the 'References' set and check that all its elements + // are present in the 'Targets' set. For a missing element, mark its + // parents as missing. + static void markMissingParents(const LVScopes *References, + const LVScopes *Targets, + bool TraverseChildren); + + // Checks if the current scope is contained within the target scope. + // Depending on the result, the callback may be performed. + virtual void markMissingParents(const LVScope *Target, bool TraverseChildren); + + // Returns true if the current scope and the given 'Scope' have the + // same number of children. + virtual bool equalNumberOfChildren(const LVScope *Scope) const; + + // Returns true if current scope is logically equal to the given 'Scope'. + virtual bool equals(const LVScope *Scope) const; + + // Returns true if the given 'References' are logically equal to the + // given 'Targets'. + static bool equals(const LVScopes *References, const LVScopes *Targets); + + // For the given 'Scopes' returns a scope that is logically equal + // to the current scope; otherwise 'nullptr'. + virtual LVScope *findEqualScope(const LVScopes *Scopes) const; + + // Report the current scope as missing or added during comparison. + void report(LVComparePass Pass) override; + static LVScopeDispatch &getDispatch() { return Dispatch; } void print(raw_ostream &OS, bool Full = true) const override; @@ -308,6 +344,13 @@ EncodedArgsIndex = getStringPool().getIndex(EncodedArgs); } + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + + // For the given 'Scopes' returns a scope that is logically equal + // to the current scope; otherwise 'nullptr'. + LVScope *findEqualScope(const LVScopes *Scopes) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -322,6 +365,9 @@ LVScopeAlias &operator=(const LVScopeAlias &) = delete; ~LVScopeAlias() = default; + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -335,6 +381,9 @@ void resolveExtra() override; + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -510,7 +559,7 @@ // A new element has been added to the scopes tree. Take the following steps: // Increase the added element counters, for printing summary. - // Notify the Reader if element comparison. + // During comparison notify the Reader of the new element. void addedElement(LVLine *Line); void addedElement(LVScope *Scope); void addedElement(LVSymbol *Symbol); @@ -518,6 +567,9 @@ void addSize(LVScope *Scope, LVOffset Lower, LVOffset Upper); + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + void print(raw_ostream &OS, bool Full = true) const override; void printExtra(raw_ostream &OS, bool Full = true) const override; void printWarnings(raw_ostream &OS, bool Full = true) const override; @@ -532,6 +584,9 @@ LVScopeEnumeration &operator=(const LVScopeEnumeration &) = delete; ~LVScopeEnumeration() = default; + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -544,6 +599,9 @@ LVScopeFormalPack &operator=(const LVScopeFormalPack &) = delete; ~LVScopeFormalPack() = default; + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -589,6 +647,13 @@ void resolveExtra() override; void resolveReferences() override; + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + + // For the given 'Scopes' returns a scope that is logically equal + // to the current scope; otherwise 'nullptr'. + LVScope *findEqualScope(const LVScopes *Scopes) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -625,6 +690,13 @@ void resolveExtra() override; + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + + // For the given 'Scopes' returns a scope that is logically equal + // to the current scope; otherwise 'nullptr'. + LVScope *findEqualScope(const LVScopes *Scopes) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -659,6 +731,13 @@ setReference(static_cast(Element)); } + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + + // For the given 'Scopes' returns a scope that is logically equal + // to the current scope; otherwise 'nullptr'. + LVScope *findEqualScope(const LVScopes *Scopes) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -682,6 +761,9 @@ // Process the collected location, ranges and calculate coverage. void processRangeInformation(); + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + void print(raw_ostream &OS, bool Full = true) const override; void printExtra(raw_ostream &OS, bool Full = true) const override; Error doPrintMatches(bool Split, raw_ostream &OS, @@ -697,6 +779,9 @@ LVScopeTemplatePack &operator=(const LVScopeTemplatePack &) = delete; ~LVScopeTemplatePack() = default; + // Returns true if current scope is logically equal to the given 'Scope'. + bool equals(const LVScope *Scope) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h @@ -63,6 +63,9 @@ LVAutoLocations::iterator addLocationGap(LVAutoLocations::iterator Pos, LVAddress LowPC, LVAddress HighPC); + // Find the current symbol in the given 'Targets'. + LVSymbol *findIn(const LVSymbols *Targets) const; + public: LVSymbol() : LVElement(LVSubclassID::LV_SYMBOL) { setIsSymbol(); @@ -157,6 +160,27 @@ static LVSymbolDispatch &getDispatch() { return Dispatch; } + static bool parametersMatch(const LVSymbols *References, + const LVSymbols *Targets); + + static void getParameters(const LVSymbols *Symbols, LVSymbols *Parameters); + + // Iterate through the 'References' set and check that all its elements + // are present in the 'Targets' set. For a missing element, mark its + // parents as missing. + static void markMissingParents(const LVSymbols *References, + const LVSymbols *Targets); + + // Returns true if current type is logically equal to the given 'Symbol'. + bool equals(const LVSymbol *Symbol) const; + + // Returns true if the given 'References' are logically equal to the + // given 'Targets'. + static bool equals(const LVSymbols *References, const LVSymbols *Targets); + + // Report the current symbol as missing or added during comparison. + void report(LVComparePass Pass) override; + void print(raw_ostream &OS, bool Full = true) const override; void printExtra(raw_ostream &OS, bool Full = true) const override; diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h @@ -56,6 +56,9 @@ LVProperties Properties; static LVTypeDispatch Dispatch; + // Find the current type in the given 'Targets'. + LVType *findIn(const LVTypes *Targets) const; + public: LVType() : LVElement(LVSubclassID::LV_TYPE) { setIsType(); } LVType(const LVType &) = delete; @@ -111,6 +114,28 @@ static LVTypeDispatch &getDispatch() { return Dispatch; } + static bool parametersMatch(const LVTypes *References, + const LVTypes *Targets); + + static void getParameters(const LVTypes *Types, LVTypes *TypesParam, + LVScopes *ScopesParam); + + // Iterate through the 'References' set and check that all its elements + // are present in the 'Targets' set. For a missing element, mark its + // parents as missing. + static void markMissingParents(const LVTypes *References, + const LVTypes *Targets); + + // Returns true if current type is logically equal to the given 'Type'. + virtual bool equals(const LVType *Type) const; + + // Returns true if the given 'References' are logically equal to the + // given 'Targets'. + static bool equals(const LVTypes *References, const LVTypes *Targets); + + // Report the current type as missing or added during comparison. + void report(LVComparePass Pass) override; + void print(raw_ostream &OS, bool Full = true) const override; void printExtra(raw_ostream &OS, bool Full = true) const override; @@ -136,6 +161,9 @@ void resolveExtra() override; + // Returns true if current type is logically equal to the given 'Type'. + bool equals(const LVType *Type) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -162,6 +190,9 @@ } size_t getValueIndex() const override { return ValueIndex; } + // Returns true if current type is logically equal to the given 'Type'. + bool equals(const LVType *Type) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -173,6 +204,9 @@ LVTypeImport &operator=(const LVTypeImport &) = delete; ~LVTypeImport() = default; + // Returns true if current type is logically equal to the given 'Type'. + bool equals(const LVType *Type) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -199,6 +233,9 @@ // Encode the specific template argument. void encodeTemplateArgument(std::string &Name) const override; + // Returns true if current type is logically equal to the given 'Type'. + bool equals(const LVType *Type) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; @@ -241,6 +278,9 @@ void resolveExtra() override; + // Returns true if current type is logically equal to the given 'Type'. + bool equals(const LVType *Type) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; }; diff --git a/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt b/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt --- a/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt +++ b/llvm/lib/DebugInfo/LogicalView/CMakeLists.txt @@ -4,6 +4,7 @@ endmacro() add_lv_impl_folder(Core + Core/LVCompare.cpp Core/LVElement.cpp Core/LVLine.cpp Core/LVLocation.cpp diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp @@ -0,0 +1,428 @@ +//===-- LVCompare.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This implements the LVCompare class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Compare" + +namespace { + +enum class LVCompareItem { Scope, Symbol, Type, Line, Total }; +enum class LVCompareIndex { Header, Expected, Missing, Added }; +using LVCompareEntry = std::tuple; +using LVCompareInfo = std::map; +LVCompareInfo Results = { + {LVCompareItem::Line, LVCompareEntry("Lines", 0, 0, 0)}, + {LVCompareItem::Scope, LVCompareEntry("Scopes", 0, 0, 0)}, + {LVCompareItem::Symbol, LVCompareEntry("Symbols", 0, 0, 0)}, + {LVCompareItem::Type, LVCompareEntry("Types", 0, 0, 0)}, + {LVCompareItem::Total, LVCompareEntry("Total", 0, 0, 0)}}; +static LVCompareInfo::iterator IterTotal = Results.end(); + +constexpr unsigned getHeader() { + return static_cast(LVCompareIndex::Header); +} +constexpr unsigned getExpected() { + return static_cast(LVCompareIndex::Expected); +} +constexpr unsigned getMissing() { + return static_cast(LVCompareIndex::Missing); +} +constexpr unsigned getAdded() { + return static_cast(LVCompareIndex::Added); +} + +LVCompare *CurrentComparator = nullptr; + +void zeroResults() { + // In case the same reader instance is used. + for (LVCompareInfo::reference Entry : Results) { + std::get(Entry.second) = 0; + std::get(Entry.second) = 0; + std::get(Entry.second) = 0; + } + IterTotal = Results.find(LVCompareItem::Total); + assert(IterTotal != Results.end()); +} + +LVCompareInfo::iterator getResultsEntry(LVElement *Element) { + LVCompareItem Kind; + if (Element->getIsLine()) + Kind = LVCompareItem::Line; + else if (Element->getIsScope()) + Kind = LVCompareItem::Scope; + else if (Element->getIsSymbol()) + Kind = LVCompareItem::Symbol; + else + Kind = LVCompareItem::Type; + + // Used to update the expected, missing or added entry for the given kind. + LVCompareInfo::iterator Iter = Results.find(Kind); + assert(Iter != Results.end()); + return Iter; +} + +void updateExpected(LVElement *Element) { + LVCompareInfo::iterator Iter = getResultsEntry(Element); + // Update total for expected. + ++std::get(IterTotal->second); + // Update total for specific element kind. + ++std::get(Iter->second); +} + +void updateMissingOrAdded(LVElement *Element, LVComparePass Pass) { + LVCompareInfo::iterator Iter = getResultsEntry(Element); + if (Pass == LVComparePass::Missing) { + ++std::get(IterTotal->second); + ++std::get(Iter->second); + } else { + ++std::get(IterTotal->second); + ++std::get(Iter->second); + } +} + +} // namespace + +LVCompare &LVCompare::getInstance() { + static LVCompare DefaultComparator(outs()); + return CurrentComparator ? *CurrentComparator : DefaultComparator; +} + +void LVCompare::setInstance(LVCompare *Comparator) { + CurrentComparator = Comparator; +} + +LVCompare::LVCompare(raw_ostream &OS) : OS(OS) { + PrintLines = options().getPrintLines(); + PrintSymbols = options().getPrintSymbols(); + PrintTypes = options().getPrintTypes(); + PrintScopes = + options().getPrintScopes() || PrintLines || PrintSymbols || PrintTypes; +} + +Error LVCompare::execute(LVReader *ReferenceReader, LVReader *TargetReader) { + setInstance(this); + // In the case of added elements, the 'Reference' reader will be modified; + // those elements will be added to it. Update the current reader instance. + LVReader::setInstance(ReferenceReader); + + auto PrintHeader = [this](LVScopeRoot *LHS, LVScopeRoot *RHS) { + LLVM_DEBUG({ + dbgs() << "[Reference] " << LHS->getName() << "\n" + << "[Target] " << RHS->getName() << "\n"; + }); + OS << "\nReference: " << formattedName(LHS->getName()) << "\n" + << "Target: " << formattedName(RHS->getName()) << "\n"; + }; + + // We traverse the given scopes tree ('Reference' and 'Target') twice. + // The first time we look for missing items from the 'Reference' and the + // second time we look for items added to the 'Target'. + // The comparison test includes the name, lexical level, type, source + // location, etc. + LVScopeRoot *ReferenceRoot = ReferenceReader->getScopesRoot(); + LVScopeRoot *TargetRoot = TargetReader->getScopesRoot(); + ReferenceRoot->setIsInCompare(); + TargetRoot->setIsInCompare(); + + // Reset possible previous results. + zeroResults(); + + if (options().getCompareContext()) { + // Perform a logical view comparison as a whole unit. We start at the + // root reference; at each scope an equal test is applied to its children. + // If a difference is found, the current path is marked as missing. + auto CompareViews = [this](LVScopeRoot *LHS, LVScopeRoot *RHS) -> Error { + LHS->markMissingParents(RHS, /*TraverseChildren=*/true); + if (LHS->getIsMissingLink() && options().getReportAnyView()) { + // As we are printing a missing tree, enable formatting. + options().setPrintFormatting(); + OS << "\nMissing Tree:\n"; + if (Error Err = LHS->doPrint(/*Split=*/false, /*Match=*/false, + /*Print=*/true, OS)) + return Err; + options().resetPrintFormatting(); + } + + return Error::success(); + }; + + // If the user has requested printing details for the comparison, we + // disable the indentation and the added/missing tags ('+'/'-'), as the + // details are just a list of elements. + options().resetPrintFormatting(); + + PrintHeader(ReferenceRoot, TargetRoot); + Reader = ReferenceReader; + if (Error Err = CompareViews(ReferenceRoot, TargetRoot)) + return Err; + FirstMissing = true; + ReferenceRoot->report(LVComparePass::Missing); + + PrintHeader(TargetRoot, ReferenceRoot); + Reader = TargetReader; + if (Error Err = CompareViews(TargetRoot, ReferenceRoot)) + return Err; + FirstMissing = true; + TargetRoot->report(LVComparePass::Added); + + options().setPrintFormatting(); + + // Display a summary with the elements missing and/or added. + printSummary(); + } else { + // Perform logical elements comparison. An equal test is apply to each + // element. If a difference is found, the reference element is marked as + // 'missing'. + // The final comparison result will show the 'Reference' scopes tree, + // having both missing and added elements. + using LVScopeLink = std::map; + LVScopeLink ScopeLinks; + auto CompareReaders = [&](LVReader *LHS, LVReader *RHS, LVElements &Set, + LVComparePass Pass) -> Error { + auto FindMatch = [&](auto &References, auto &Targets, + const char *Category) -> Error { + LVElements Elements; + for (LVElement *Reference : References) { + // Report elements that can be printed; ignore logical elements that + // have qualifiers. + if (Reference->getIncludeInPrint()) { + if (Pass == LVComparePass::Missing) + updateExpected(Reference); + Reference->setIsInCompare(); + LVElement *CurrentTarget = nullptr; + if (std::any_of(Targets.begin(), Targets.end(), + [&](auto Target) -> bool { + CurrentTarget = Target; + return Reference->equals(Target); + })) { + if (Pass == LVComparePass::Missing && Reference->getIsScope()) { + // If the elements being compared are scopes and are a match, + // they are recorded, to be used when creating the augmented + // tree, as insertion points for the "added" items. + ScopeLinks.emplace(static_cast(CurrentTarget), + static_cast(Reference)); + } + } else { + // Element is missing or added. + Pass == LVComparePass::Missing ? Reference->setIsMissing() + : Reference->setIsAdded(); + Elements.push_back(Reference); + updateMissingOrAdded(Reference, Pass); + // Record missing/added element. + addPassEntry(Reader, Reference, Pass); + } + } + } + if (Pass == LVComparePass::Added) + // Record all the current missing elements for this category. + Set.insert(Set.end(), Elements.begin(), Elements.end()); + if (options().getReportList()) { + if (Elements.size()) { + OS << "\n(" << Elements.size() << ") " + << (Pass == LVComparePass::Missing ? "Missing" : "Added") << " " + << Category << ":\n"; + for (const LVElement *Element : Elements) { + if (Error Err = Element->doPrint(/*Split=*/false, /*Match=*/false, + /*Print=*/true, OS)) + return Err; + } + } + } + + return Error::success(); + }; + + // First compare the scopes, so they will be inserted at the front of + // the missing elements list. When they are moved, their children are + // moved as well and no additional work is required. + if (options().getCompareScopes()) + if (Error Err = FindMatch(LHS->getScopes(), RHS->getScopes(), "Scopes")) + return Err; + if (options().getCompareSymbols()) + if (Error Err = + FindMatch(LHS->getSymbols(), RHS->getSymbols(), "Symbols")) + return Err; + if (options().getCompareTypes()) + if (Error Err = FindMatch(LHS->getTypes(), RHS->getTypes(), "Types")) + return Err; + if (options().getCompareLines()) + if (Error Err = FindMatch(LHS->getLines(), RHS->getLines(), "Lines")) + return Err; + + return Error::success(); + }; + + // If the user has requested printing details for the comparison, we + // disable the indentation and the added/missing tags ('+'/'-'), as the + // details are just a list of elements. + options().resetPrintFormatting(); + + PrintHeader(ReferenceRoot, TargetRoot); + // Include the root in the expected count. + updateExpected(ReferenceRoot); + + LVElements ElementsToAdd; + Reader = ReferenceReader; + if (Error Err = CompareReaders(ReferenceReader, TargetReader, ElementsToAdd, + LVComparePass::Missing)) + return Err; + Reader = TargetReader; + if (Error Err = CompareReaders(TargetReader, ReferenceReader, ElementsToAdd, + LVComparePass::Added)) + return Err; + + LLVM_DEBUG({ + dbgs() << "\nReference/Target Scope links:\n"; + for (LVScopeLink::const_reference Entry : ScopeLinks) + dbgs() << "Source: " << hexSquareString(Entry.first->getOffset()) << " " + << "Destination: " << hexSquareString(Entry.second->getOffset()) + << "\n"; + dbgs() << "\n"; + }); + + // Add the 'missing' elements from the 'Target' into the 'Reference'. + // First insert the missing scopes, as they include any missing children. + LVScope *Parent = nullptr; + for (LVElement *Element : ElementsToAdd) { + LLVM_DEBUG({ + dbgs() << "Element to Insert: " << hexSquareString(Element->getOffset()) + << ", Parent: " + << hexSquareString(Element->getParentScope()->getOffset()) + << "\n"; + }); + // Skip already inserted elements. They were inserted, if their parents + // were missing. When inserting them, all the children are moved. + if (Element->getHasMoved()) + continue; + + // We need to find an insertion point in the reference scopes tree. + Parent = Element->getParentScope(); + if (ScopeLinks.find(Parent) != ScopeLinks.end()) { + LVScope *InsertionPoint = ScopeLinks[Parent]; + LLVM_DEBUG({ + dbgs() << "Inserted at: " + << hexSquareString(InsertionPoint->getOffset()) << "\n"; + }); + if (Parent->removeElement(Element)) { + // Be sure we have a current compile unit. + getReader().setCompileUnit(InsertionPoint->getCompileUnitParent()); + InsertionPoint->addElement(Element); + Element->updateLevel(InsertionPoint, /*Moved=*/true); + } + } + } + + options().setPrintFormatting(); + + // Display the augmented reference scopes tree. + if (options().getReportAnyView()) + if (Error Err = ReferenceReader->doPrint()) + return Err; + + LLVM_DEBUG({ + dbgs() << "\nModified Reference Reader"; + if (Error Err = ReferenceReader->doPrint()) + return Err; + dbgs() << "\nModified Target Reader"; + if (Error Err = TargetReader->doPrint()) + return Err; + }); + + // Display a summary with the elements missing and/or added. + printSummary(); + } + + return Error::success(); +} + +void LVCompare::printCurrentStack() { + for (const LVScope *Scope : ScopeStack) { + Scope->printAttributes(OS); + OS << Scope->lineNumberAsString(/*ShowZero=*/true) << " " << Scope->kind() + << " " << formattedName(Scope->getName()) << "\n"; + } +} + +void LVCompare::printItem(LVElement *Element, LVComparePass Pass) { + // Record expected, missing, added. + updateExpected(Element); + updateMissingOrAdded(Element, Pass); + + // Record missing/added element. + if (Element->getIsMissing()) + addPassEntry(Reader, Element, Pass); + + if ((!PrintLines && Element->getIsLine()) || + (!PrintScopes && Element->getIsScope()) || + (!PrintSymbols && Element->getIsSymbol()) || + (!PrintTypes && Element->getIsType())) + return; + + if (Element->getIsMissing()) { + if (FirstMissing) { + OS << "\n"; + FirstMissing = false; + } + + StringRef Kind = Element->kind(); + StringRef Name = + Element->getIsLine() ? Element->getPathname() : Element->getName(); + StringRef Status = (Pass == LVComparePass::Missing) ? "Missing" : "Added"; + OS << Status << " " << Kind << " '" << Name << "'"; + if (Element->getLineNumber() > 0) + OS << " at line " << Element->getLineNumber(); + OS << "\n"; + + if (options().getReportList()) { + printCurrentStack(); + Element->printAttributes(OS); + OS << Element->lineNumberAsString(/*ShowZero=*/true) << " " << Kind << " " + << Name << "\n"; + } + } +} + +void LVCompare::printSummary() const { + if (!options().getPrintSummary()) + return; + std::string Separator = std::string(40, '-'); + auto PrintSeparator = [&]() { OS << Separator << "\n"; }; + auto PrintHeadingRow = [&](const char *T, const char *U, const char *V, + const char *W) { + OS << format("%-9s%9s %9s %9s\n", T, U, V, W); + }; + auto PrintDataRow = [&](const char *T, unsigned U, unsigned V, unsigned W) { + OS << format("%-9s%9d %9d %9d\n", T, U, V, W); + }; + + OS << "\n"; + PrintSeparator(); + PrintHeadingRow("Element", "Expected", "Missing", "Added"); + PrintSeparator(); + for (LVCompareInfo::reference Entry : Results) { + if (Entry.first == LVCompareItem::Total) + PrintSeparator(); + PrintDataRow(std::get(Entry.second), + std::get(Entry.second), + std::get(Entry.second), + std::get(Entry.second)); + } +} + +void LVCompare::print(raw_ostream &OS) const { OS << "LVCompare\n"; } diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp @@ -431,6 +431,55 @@ }); } +bool LVElement::referenceMatch(const LVElement *Element) const { + return (getHasReference() && Element->getHasReference()) || + (!getHasReference() && !Element->getHasReference()); +} + +bool LVElement::equals(const LVElement *Element) const { + // The minimum factors that must be the same for an equality are: + // line number, level, name, qualified name and filename. + LLVM_DEBUG({ + dbgs() << "\n[Element::equals]\n"; + if (options().getAttributeOffset()) { + dbgs() << "Reference: " << hexSquareString(getOffset()) << "\n"; + dbgs() << "Target : " << hexSquareString(Element->getOffset()) << "\n"; + } + dbgs() << "Reference: " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << ", " + << "Qualified = " << formattedName(getQualifiedName()) << "\n" + << "Target : " + << "Kind = " << formattedKind(Element->kind()) << ", " + << "Name = " << formattedName(Element->getName()) << ", " + << "Qualified = " << formattedName(Element->getQualifiedName()) + << "\n" + << "Reference: " + << "NameIndex = " << getNameIndex() << ", " + << "QualifiedNameIndex = " << getQualifiedNameIndex() << ", " + << "FilenameIndex = " << getFilenameIndex() << "\n" + << "Target : " + << "NameIndex = " << Element->getNameIndex() << ", " + << "QualifiedNameIndex = " << Element->getQualifiedNameIndex() + << ", " + << "FilenameIndex = " << Element->getFilenameIndex() << "\n"; + }); + if ((getLineNumber() != Element->getLineNumber()) || + (getLevel() != Element->getLevel())) + return false; + + if ((getQualifiedNameIndex() != Element->getQualifiedNameIndex()) || + (getNameIndex() != Element->getNameIndex()) || + (getFilenameIndex() != Element->getFilenameIndex())) + return false; + + if (!getType() && !Element->getType()) + return true; + if (getType() && Element->getType()) + return getType()->equals(Element->getType()); + return false; +} + // Print the FileName Index. void LVElement::printFileIndex(raw_ostream &OS, bool Full) const { if (options().getPrintFormatting() && options().getAttributeAnySource() && diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" using namespace llvm; @@ -65,6 +66,77 @@ : (" - "); } +void LVLine::markMissingParents(const LVLines *References, + const LVLines *Targets) { + if (!(References && Targets)) + return; + + LLVM_DEBUG({ + dbgs() << "\n[LVLine::markMissingParents]\n"; + for (const LVLine *Reference : *References) + dbgs() << "References: " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Line = " << Reference->getLineNumber() << "\n"; + for (const LVLine *Target : *Targets) + dbgs() << "Targets : " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Line = " << Target->getLineNumber() << "\n"; + }); + + for (LVLine *Reference : *References) { + LLVM_DEBUG({ + dbgs() << "Search Reference: Line = " << Reference->getLineNumber() + << "\n"; + }); + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); + } +} + +LVLine *LVLine::findIn(const LVLines *Targets) const { + if (!Targets) + return nullptr; + + LLVM_DEBUG({ + dbgs() << "\n[LVLine::findIn]\n" + << "Reference: " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Line = " << getLineNumber() << "\n"; + for (const LVLine *Target : *Targets) + dbgs() << "Target : " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Line = " << Target->getLineNumber() << "\n"; + }); + + for (LVLine *Line : *Targets) + if (equals(Line)) + return Line; + + return nullptr; +} + +bool LVLine::equals(const LVLine *Line) const { + return LVElement::equals(Line); +} + +bool LVLine::equals(const LVLines *References, const LVLines *Targets) { + if (!References && !Targets) + return true; + if (References && Targets && References->size() == Targets->size()) { + for (const LVLine *Reference : *References) + if (!Reference->findIn(Targets)) + return false; + return true; + } + return false; +} + +void LVLine::report(LVComparePass Pass) { + getComparator().printItem(this, Pass); +} + void LVLine::print(raw_ostream &OS, bool Full) const { if (getReader().doPrintLine(this)) { getReaderCompileUnit()->incrementPrintedLines(); @@ -118,6 +190,12 @@ return String; } +bool LVLineDebug::equals(const LVLine *Line) const { + if (!LVLine::equals(Line)) + return false; + return getFilenameIndex() == Line->getFilenameIndex(); +} + void LVLineDebug::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()); @@ -133,6 +211,10 @@ //===----------------------------------------------------------------------===// // Assembler line extracted from the ELF .text section. //===----------------------------------------------------------------------===// +bool LVLineAssembler::equals(const LVLine *Line) const { + return LVLine::equals(Line); +} + void LVLineAssembler::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()); OS << " " << formattedName(getName()); diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp @@ -95,6 +95,18 @@ setLevel(Symbol->getLevel() + 1); } +void LVObject::markBranchAsMissing() { + // Mark the current object as 'missing'; then traverse the parents chain + // marking them as 'special missing' to indicate a missing branch. They + // can not be marked as missing, because will generate incorrect reports. + LVObject *Parent = this; + Parent->setIsMissing(); + while (Parent) { + Parent->setIsMissingLink(); + Parent = Parent->getParent(); + } +} + Error LVObject::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, bool Full) const { print(OS, Full); @@ -129,6 +141,9 @@ if (options().getInternalID()) OS << hexSquareString(getID()); #endif + if (options().getCompareExecute() && + (options().getAttributeAdded() || options().getAttributeMissing())) + OS << (getIsAdded() ? '+' : getIsMissing() ? '-' : ' '); if (options().getAttributeOffset()) OS << hexSquareString(getOffset()); if (options().getAttributeLevel()) { diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -276,7 +276,8 @@ } Error LVReader::printScopes() { - if (bool DoPrint = options().getPrintExecute()) { + if (bool DoPrint = + (options().getPrintExecute() || options().getComparePrint())) { if (Error Err = createSplitFolder()) return Err; diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" #include "llvm/DebugInfo/LogicalView/Core/LVLine.h" #include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" #include "llvm/DebugInfo/LogicalView/Core/LVRange.h" @@ -827,6 +828,172 @@ return Parent; } +LVScope *LVScope::findIn(const LVScopes *Targets) const { + if (!Targets) + return nullptr; + + // In the case of overloaded functions, sometimes the DWARF used to + // describe them, does not give suficient information. Try to find a + // perfect match or mark them as possible conflicts. + LVScopes Candidates; + for (LVScope *Target : *Targets) + if (LVScope::equals(Target)) + Candidates.push_back(Target); + + LLVM_DEBUG({ + if (!Candidates.empty()) { + dbgs() << "\n[LVScope::findIn]\n" + << "Reference: " + << "Offset = " << hexSquareString(getOffset()) << ", " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const LVScope *Candidate : Candidates) + dbgs() << "Candidate: " + << "Offset = " << hexSquareString(Candidate->getOffset()) << ", " + << "Level = " << Candidate->getLevel() << ", " + << "Kind = " << formattedKind(Candidate->kind()) << ", " + << "Name = " << formattedName(Candidate->getName()) << "\n"; + } + }); + + if (!Candidates.empty()) + return (Candidates.size() == 1) + ? (equals(Candidates[0]) ? Candidates[0] : nullptr) + : findEqualScope(&Candidates); + + return nullptr; +} + +bool LVScope::equalNumberOfChildren(const LVScope *Scope) const { + // Same number of children. Take into account which elements are requested + // to be included in the comparison. + return !( + (options().getCompareScopes() && scopeCount() != Scope->scopeCount()) || + (options().getCompareSymbols() && + symbolCount() != Scope->symbolCount()) || + (options().getCompareTypes() && typeCount() != Scope->typeCount()) || + (options().getCompareLines() && lineCount() != Scope->lineCount())); +} + +void LVScope::markMissingParents(const LVScope *Target, bool TraverseChildren) { + auto SetCompareState = [&](auto *Container) { + if (Container) + for (auto *Entry : *Container) + Entry->setIsInCompare(); + }; + SetCompareState(Types); + SetCompareState(Symbols); + SetCompareState(Lines); + SetCompareState(Scopes); + + // At this point, we are ready to start comparing the current scope, once + // the compare bits have been set. + if (options().getCompareTypes() && getTypes() && Target->getTypes()) + LVType::markMissingParents(getTypes(), Target->getTypes()); + if (options().getCompareSymbols() && getSymbols() && Target->getSymbols()) + LVSymbol::markMissingParents(getSymbols(), Target->getSymbols()); + if (options().getCompareLines() && getLines() && Target->getLines()) + LVLine::markMissingParents(getLines(), Target->getLines()); + if (getScopes() && Target->getScopes()) + LVScope::markMissingParents(getScopes(), Target->getScopes(), + TraverseChildren); +} + +void LVScope::markMissingParents(const LVScopes *References, + const LVScopes *Targets, + bool TraverseChildren) { + if (!(References && Targets)) + return; + + LLVM_DEBUG({ + dbgs() << "\n[LVScope::markMissingParents]\n"; + for (const LVScope *Reference : *References) + dbgs() << "References: " + << "Offset = " << hexSquareString(Reference->getOffset()) << ", " + << "Level = " << Reference->getLevel() << ", " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const LVScope *Target : *Targets) + dbgs() << "Targets : " + << "Offset = " << hexSquareString(Target->getOffset()) << ", " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (LVScope *Reference : *References) { + // Don't process 'Block' scopes, as we can't identify them. + if (Reference->getIsBlock() || Reference->getIsGeneratedName()) + continue; + + LLVM_DEBUG({ + dbgs() << "\nSearch Reference: " + << "Offset = " << hexSquareString(Reference->getOffset()) << " " + << "Name = " << formattedName(Reference->getName()) << "\n"; + }); + LVScope *Target = Reference->findIn(Targets); + if (Target) { + LLVM_DEBUG({ + dbgs() << "\nFound Target: " + << "Offset = " << hexSquareString(Target->getOffset()) << " " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + if (TraverseChildren) + Reference->markMissingParents(Target, TraverseChildren); + } else { + LLVM_DEBUG({ + dbgs() << "Missing Reference: " + << "Offset = " << hexSquareString(Reference->getOffset()) << " " + << "Name = " << formattedName(Reference->getName()) << "\n"; + }); + Reference->markBranchAsMissing(); + } + } +} + +bool LVScope::equals(const LVScope *Scope) const { + if (!LVElement::equals(Scope)) + return false; + // For lexical scopes, check if their parents are the same. + if (getIsLexicalBlock() && Scope->getIsLexicalBlock()) + return getParentScope()->equals(Scope->getParentScope()); + return true; +} + +LVScope *LVScope::findEqualScope(const LVScopes *Scopes) const { + assert(Scopes && "Scopes must not be nullptr"); + for (LVScope *Scope : *Scopes) + if (equals(Scope)) + return Scope; + return nullptr; +} + +bool LVScope::equals(const LVScopes *References, const LVScopes *Targets) { + if (!References && !Targets) + return true; + if (References && Targets && References->size() == Targets->size()) { + for (const LVScope *Reference : *References) + if (!Reference->findIn(Targets)) + return false; + return true; + } + return false; +} + +void LVScope::report(LVComparePass Pass) { + getComparator().printItem(this, Pass); + getComparator().push(this); + if (Children) + for (LVElement *Element : *Children) + Element->report(Pass); + + if (Lines) + for (LVLine *Line : *Lines) + Line->report(Pass); + getComparator().pop(); +} + void LVScope::printActiveRanges(raw_ostream &OS, bool Full) const { if (options().getPrintFormatting() && options().getAttributeRange() && Ranges) { @@ -871,6 +1038,33 @@ //===----------------------------------------------------------------------===// // DWARF Union/Structure/Class. //===----------------------------------------------------------------------===// +bool LVScopeAggregate::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + if (!equalNumberOfChildren(Scope)) + return false; + + // Check if the parameters match in the case of templates. + if (!LVType::parametersMatch(getTypes(), Scope->getTypes())) + return false; + + if (!isNamed() && !Scope->isNamed()) + // In the case of unnamed union/structure/class compare the file name. + if (getFilenameIndex() != Scope->getFilenameIndex()) + return false; + + return true; +} + +LVScope *LVScopeAggregate::findEqualScope(const LVScopes *Scopes) const { + assert(Scopes && "Scopes must not be nullptr"); + for (LVScope *Scope : *Scopes) + if (equals(Scope)) + return Scope; + return nullptr; +} + void LVScopeAggregate::printExtra(raw_ostream &OS, bool Full) const { LVScope::printExtra(OS, Full); if (Full) { @@ -885,6 +1079,12 @@ //===----------------------------------------------------------------------===// // DWARF Template alias. //===----------------------------------------------------------------------===// +bool LVScopeAlias::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return equalNumberOfChildren(Scope); +} + void LVScopeAlias::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> " << typeOffsetAsString() @@ -965,6 +1165,21 @@ setName(ArrayInfo.str()); } +bool LVScopeArray::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + if (!equalNumberOfChildren(Scope)) + return false; + + // Despite the arrays are encoded, to reflect the dimensions, we have to + // check the subranges, in order to determine if they are the same. + if (!LVType::equals(getTypes(), Scope->getTypes())) + return false; + + return true; +} + void LVScopeArray::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " " << typeOffsetAsString() << formattedName(getName()) << "\n"; @@ -1057,6 +1272,13 @@ return getStringPool().getString(Filenames[Index - 1]); } +bool LVScopeCompileUnit::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + return getNameIndex() == Scope->getNameIndex(); +} + void LVScopeCompileUnit::incrementPrintedLines() { options().getSelectExecute() ? ++Found.Lines : ++Printed.Lines; } @@ -1090,10 +1312,23 @@ // A new element has been added to the scopes tree. Take the following steps: // Increase the added element counters, for printing summary. -void LVScopeCompileUnit::addedElement(LVLine *Line) { increment(Line); } -void LVScopeCompileUnit::addedElement(LVScope *Scope) { increment(Scope); } -void LVScopeCompileUnit::addedElement(LVSymbol *Symbol) { increment(Symbol); } -void LVScopeCompileUnit::addedElement(LVType *Type) { increment(Type); } +// During comparison notify the Reader of the new element. +void LVScopeCompileUnit::addedElement(LVLine *Line) { + increment(Line); + getReader().notifyAddedElement(Line); +} +void LVScopeCompileUnit::addedElement(LVScope *Scope) { + increment(Scope); + getReader().notifyAddedElement(Scope); +} +void LVScopeCompileUnit::addedElement(LVSymbol *Symbol) { + increment(Symbol); + getReader().notifyAddedElement(Symbol); +} +void LVScopeCompileUnit::addedElement(LVType *Type) { + increment(Type); + getReader().notifyAddedElement(Type); +} // Record unsuported DWARF tags. void LVScopeCompileUnit::addDebugTag(dwarf::Tag Target, LVOffset Offset) { @@ -1458,6 +1693,12 @@ //===----------------------------------------------------------------------===// // DWARF enumeration (DW_TAG_enumeration_type). //===----------------------------------------------------------------------===// +bool LVScopeEnumeration::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return equalNumberOfChildren(Scope); +} + void LVScopeEnumeration::printExtra(raw_ostream &OS, bool Full) const { // Print the full type name. OS << formattedKind(kind()) << " " << (getIsEnumClass() ? "class " : "") @@ -1471,6 +1712,12 @@ //===----------------------------------------------------------------------===// // DWARF formal parameter pack (DW_TAG_GNU_formal_parameter_pack). //===----------------------------------------------------------------------===// +bool LVScopeFormalPack::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return equalNumberOfChildren(Scope); +} + void LVScopeFormalPack::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; } @@ -1528,6 +1775,51 @@ resolveTemplate(); } +bool LVScopeFunction::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + // When comparing logical elements, ignore any difference in the children. + if (options().getCompareContext() && !equalNumberOfChildren(Scope)) + return false; + + // Check if the linkage name matches. + if (getLinkageNameIndex() != Scope->getLinkageNameIndex()) + return false; + + // Check if the parameters match in the case of templates. + if (!LVType::parametersMatch(getTypes(), Scope->getTypes())) + return false; + + // Check if the arguments match. + if (!LVSymbol::parametersMatch(getSymbols(), Scope->getSymbols())) + return false; + + // Check if the lines match. + if (options().getCompareLines() && + !LVLine::equals(getLines(), Scope->getLines())) + return false; + + // Check if any reference is the same. + if (!referenceMatch(Scope)) + return false; + + if (getReference() && !getReference()->equals(Scope->getReference())) + return false; + + return true; +} + +LVScope *LVScopeFunction::findEqualScope(const LVScopes *Scopes) const { + assert(Scopes && "Scopes must not be nullptr"); + // Go through candidates and try to find a best match. + for (LVScope *Scope : *Scopes) + // Match arguments, children, lines, references. + if (equals(Scope)) + return Scope; + return nullptr; +} + void LVScopeFunction::printExtra(raw_ostream &OS, bool Full) const { LVScope *Reference = getReference(); @@ -1570,6 +1862,27 @@ resolveTemplate(); } +bool LVScopeFunctionInlined::equals(const LVScope *Scope) const { + if (!LVScopeFunction::equals(Scope)) + return false; + + // Check if any reference is the same. + if (getHasDiscriminator() && Scope->getHasDiscriminator()) + if (getDiscriminator() != Scope->getDiscriminator()) + return false; + + // Check the call site information. + if (getCallFilenameIndex() != Scope->getCallFilenameIndex() || + getCallLineNumber() != Scope->getCallLineNumber()) + return false; + + return true; +} + +LVScope *LVScopeFunctionInlined::findEqualScope(const LVScopes *Scopes) const { + return LVScopeFunction::findEqualScope(Scopes); +} + void LVScopeFunctionInlined::printExtra(raw_ostream &OS, bool Full) const { LVScopeFunction::printExtra(OS, Full); } @@ -1612,6 +1925,32 @@ //===----------------------------------------------------------------------===// // DWARF namespace (DW_TAG_namespace). //===----------------------------------------------------------------------===// +bool LVScopeNamespace::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + + if (!equalNumberOfChildren(Scope)) + return false; + + // Check if any reference is the same. + if (!referenceMatch(Scope)) + return false; + + if (getReference() && !getReference()->equals(Scope->getReference())) + return false; + + return true; +} + +LVScope *LVScopeNamespace::findEqualScope(const LVScopes *Scopes) const { + assert(Scopes && "Scopes must not be nullptr"); + // Go through candidates and try to find a best match. + for (LVScope *Scope : *Scopes) + if (equals(Scope)) + return Scope; + return nullptr; +} + void LVScopeNamespace::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; @@ -1640,6 +1979,10 @@ } } +bool LVScopeRoot::equals(const LVScope *Scope) const { + return LVScope::equals(Scope); +} + void LVScopeRoot::print(raw_ostream &OS, bool Full) const { OS << "\nLogical View:\n"; LVScope::print(OS, Full); @@ -1695,6 +2038,12 @@ //===----------------------------------------------------------------------===// // DWARF template parameter pack (DW_TAG_GNU_template_parameter_pack). //===----------------------------------------------------------------------===// +bool LVScopeTemplatePack::equals(const LVScope *Scope) const { + if (!LVScope::equals(Scope)) + return false; + return equalNumberOfChildren(Scope); +} + void LVScopeTemplatePack::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; } diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" #include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" @@ -279,6 +280,110 @@ return getName(); } +void LVSymbol::markMissingParents(const LVSymbols *References, + const LVSymbols *Targets) { + if (!(References && Targets)) + return; + + LLVM_DEBUG({ + dbgs() << "\n[LVSymbol::markMissingParents]\n"; + for (const LVSymbol *Reference : *References) + dbgs() << "References: " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const LVSymbol *Target : *Targets) + dbgs() << "Targets : " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (LVSymbol *Reference : *References) { + LLVM_DEBUG({ + dbgs() << "Search Reference: Name = " + << formattedName(Reference->getName()) << "\n"; + }); + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); + } +} + +LVSymbol *LVSymbol::findIn(const LVSymbols *Targets) const { + if (!Targets) + return nullptr; + + LLVM_DEBUG({ + dbgs() << "\n[LVSymbol::findIn]\n" + << "Reference: " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const LVSymbol *Target : *Targets) + dbgs() << "Target : " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (LVSymbol *Target : *Targets) + if (equals(Target)) + return Target; + + return nullptr; +} + +// Check for a match on the arguments of a function. +bool LVSymbol::parametersMatch(const LVSymbols *References, + const LVSymbols *Targets) { + if (!References && !Targets) + return true; + if (References && Targets) { + LVSymbols ReferenceParams; + getParameters(References, &ReferenceParams); + LVSymbols TargetParams; + getParameters(Targets, &TargetParams); + return LVSymbol::equals(&ReferenceParams, &TargetParams); + } + return false; +} + +// Return the symbols which are parameters. +void LVSymbol::getParameters(const LVSymbols *Symbols, LVSymbols *Parameters) { + if (Symbols) + for (LVSymbol *Symbol : *Symbols) + if (Symbol->getIsParameter()) + Parameters->push_back(Symbol); +} + +bool LVSymbol::equals(const LVSymbol *Symbol) const { + if (!LVElement::equals(Symbol)) + return false; + + // Check if any reference is the same. + if (!referenceMatch(Symbol)) + return false; + + if (getReference() && !getReference()->equals(Symbol->getReference())) + return false; + + return true; +} + +bool LVSymbol::equals(const LVSymbols *References, const LVSymbols *Targets) { + if (!References && !Targets) + return true; + if (References && Targets && References->size() == Targets->size()) { + for (const LVSymbol *Reference : *References) + if (!Reference->findIn(Targets)) + return false; + return true; + } + return false; +} + +void LVSymbol::report(LVComparePass Pass) { + getComparator().printItem(this, Pass); +} + void LVSymbol::printLocations(raw_ostream &OS, bool Full) const { if (Locations) for (const LVLocation *Location : *Locations) diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp --- a/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" @@ -169,6 +170,124 @@ return getName(); } +void LVType::markMissingParents(const LVTypes *References, + const LVTypes *Targets) { + if (!(References && Targets)) + return; + + LLVM_DEBUG({ + dbgs() << "\n[LVType::markMissingParents]\n"; + for (const LVType *Reference : *References) + dbgs() << "References: " + << "Kind = " << formattedKind(Reference->kind()) << ", " + << "Name = " << formattedName(Reference->getName()) << "\n"; + for (const LVType *Target : *Targets) + dbgs() << "Targets : " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (LVType *Reference : *References) { + LLVM_DEBUG({ + dbgs() << "Search Reference: Name = " + << formattedName(Reference->getName()) << "\n"; + }); + if (!Reference->findIn(Targets)) + Reference->markBranchAsMissing(); + } +} + +LVType *LVType::findIn(const LVTypes *Targets) const { + if (!Targets) + return nullptr; + + LLVM_DEBUG({ + dbgs() << "\n[LVType::findIn]\n" + << "Reference: " + << "Level = " << getLevel() << ", " + << "Kind = " << formattedKind(kind()) << ", " + << "Name = " << formattedName(getName()) << "\n"; + for (const LVType *Target : *Targets) + dbgs() << "Target : " + << "Level = " << Target->getLevel() << ", " + << "Kind = " << formattedKind(Target->kind()) << ", " + << "Name = " << formattedName(Target->getName()) << "\n"; + }); + + for (LVType *Target : *Targets) + if (equals(Target)) + return Target; + + return nullptr; +} + +// Check for a match on the arguments of a function. +bool LVType::parametersMatch(const LVTypes *References, + const LVTypes *Targets) { + if (!References && !Targets) + return true; + if (References && Targets) { + LVTypes ReferenceTypes; + LVScopes ReferenceScopes; + getParameters(References, &ReferenceTypes, &ReferenceScopes); + LVTypes TargetTypes; + LVScopes TargetScopes; + getParameters(Targets, &TargetTypes, &TargetScopes); + if (!LVType::equals(&ReferenceTypes, &TargetTypes) || + !LVScope::equals(&ReferenceScopes, &TargetScopes)) + return false; + return true; + } + return false; +} + +// Return the types which are parameters. +void LVType::getParameters(const LVTypes *Types, LVTypes *TypesParam, + LVScopes *ScopesParam) { + if (!Types) + return; + + // During a compare task, the template parameters are expanded to + // point to their real types, to avoid compare conflicts. + for (LVType *Type : *Types) { + if (!Type->getIsTemplateParam()) + continue; + if (options().getAttributeArgument()) { + LVScope *Scope = nullptr; + if (Type->getIsKindType()) + Type = Type->getTypeAsType(); + else { + if (Type->getIsKindScope()) { + Scope = Type->getTypeAsScope(); + Type = nullptr; + } + } + Type ? TypesParam->push_back(Type) : ScopesParam->push_back(Scope); + } else + TypesParam->push_back(Type); + } +} + +bool LVType::equals(const LVType *Type) const { + return LVElement::equals(Type); +} + +bool LVType::equals(const LVTypes *References, const LVTypes *Targets) { + if (!References && !Targets) + return true; + if (References && Targets && References->size() == Targets->size()) { + for (const LVType *Reference : *References) + if (!Reference->findIn(Targets)) + return false; + return true; + } + return false; +} + +void LVType::report(LVComparePass Pass) { + getComparator().printItem(this, Pass); +} + void LVType::print(raw_ostream &OS, bool Full) const { if (getIncludeInPrint() && (getIsReference() || getReader().doPrintType(this))) { @@ -229,6 +348,10 @@ Aggregate->setName(getName()); } +bool LVTypeDefinition::equals(const LVType *Type) const { + return LVType::equals(Type); +} + void LVTypeDefinition::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> " << typeOffsetAsString() @@ -238,6 +361,10 @@ //===----------------------------------------------------------------------===// // DWARF enumerator (DW_TAG_enumerator). //===----------------------------------------------------------------------===// +bool LVTypeEnumerator::equals(const LVType *Type) const { + return LVType::equals(Type); +} + void LVTypeEnumerator::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " '" << getName() << "' = " << formattedName(getValue()) << "\n"; @@ -246,6 +373,10 @@ //===----------------------------------------------------------------------===// // DWARF import (DW_TAG_imported_module / DW_TAG_imported_declaration). //===----------------------------------------------------------------------===// +bool LVTypeImport::equals(const LVType *Type) const { + return LVType::equals(Type); +} + void LVTypeImport::printExtra(raw_ostream &OS, bool Full) const { std::string Attributes = formatAttributes(virtualityString(), accessibilityString()); @@ -316,6 +447,21 @@ Name.append(getValue()); } +bool LVTypeParam::equals(const LVType *Type) const { + if (!LVType::equals(Type)) + return false; + + // Checks the kind of template argument. + if (getIsTemplateTypeParam() && Type->getIsTemplateTypeParam()) + return getType()->equals(Type->getType()); + + if ((getIsTemplateValueParam() && Type->getIsTemplateValueParam()) || + (getIsTemplateTemplateParam() && Type->getIsTemplateTemplateParam())) + return getValueIndex() == Type->getValueIndex(); + + return false; +} + void LVTypeParam::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " " << formattedName(getName()) << " -> " << typeOffsetAsString(); @@ -365,6 +511,13 @@ setName(String); } +bool LVTypeSubrange::equals(const LVType *Type) const { + if (!LVType::equals(Type)) + return false; + + return getTypeName() == Type->getTypeName() && getName() == Type->getName(); +} + void LVTypeSubrange::printExtra(raw_ostream &OS, bool Full) const { OS << formattedKind(kind()) << " -> " << typeOffsetAsString() << formattedName(getTypeName()) << " " << formattedName(getName()) << "\n"; diff --git a/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt b/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt --- a/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt @@ -4,6 +4,7 @@ add_llvm_unittest(DebugInfoLogicalViewTests CommandLineOptionsTest.cpp + CompareElementsTest.cpp SelectElementsTest.cpp LocationRangesTest.cpp LogicalElementsTest.cpp diff --git a/llvm/unittests/DebugInfo/LogicalView/CompareElementsTest.cpp b/llvm/unittests/DebugInfo/LogicalView/CompareElementsTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DebugInfo/LogicalView/CompareElementsTest.cpp @@ -0,0 +1,450 @@ +//===- llvm/unittest/DebugInfo/LogicalView/CompareElementsTest.cpp --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +//===----------------------------------------------------------------------===// +// Basic Reader functionality. +class ReaderTestCompare : public LVReader { +public: + // Types. + LVType *IntegerType = nullptr; + LVType *UnsignedType = nullptr; + LVType *GlobalType = nullptr; + LVType *LocalType = nullptr; + LVType *NestedType = nullptr; + LVTypeDefinition *TypeDefinitionOne = nullptr; + LVTypeDefinition *TypeDefinitionTwo = nullptr; + LVTypeEnumerator *EnumeratorOne = nullptr; + LVTypeEnumerator *EnumeratorTwo = nullptr; + + // Scopes. + LVScope *NestedScope = nullptr; + LVScope *InnerScope = nullptr; + LVScopeAggregate *Aggregate = nullptr; + LVScopeEnumeration *Enumeration = nullptr; + LVScopeFunction *FunctionOne = nullptr; + LVScopeFunction *FunctionTwo = nullptr; + LVScopeNamespace *Namespace = nullptr; + + // Symbols. + LVSymbol *GlobalVariable = nullptr; + LVSymbol *LocalVariable = nullptr; + LVSymbol *ClassMember = nullptr; + LVSymbol *NestedVariable = nullptr; + LVSymbol *ParameterOne = nullptr; + LVSymbol *ParameterTwo = nullptr; + + // Lines. + LVLine *LineOne = nullptr; + LVLine *LineTwo = nullptr; + LVLine *LineThree = nullptr; + +protected: + void add(LVScope *Parent, LVElement *Element); + template T *create(F Function) { + // 'Function' will update a specific kind of the logical element to + // have the ability of kind selection. + T *Element = new (std::nothrow) T(); + EXPECT_NE(Element, nullptr); + (Element->*Function)(); + return Element; + } + void set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber = 0, LVElement *Type = nullptr); + +public: + ReaderTestCompare(ScopedPrinter &W) : LVReader("", "", W) { + setInstance(this); + } + + Error createScopes() { return LVReader::createScopes(); } + Error printScopes() { return LVReader::printScopes(); } + + void createElements(); + void addElements(bool IsReference, bool IsTarget); + void initElements(); +}; + +// Helper function to add a logical element to a given scope. +void ReaderTestCompare::add(LVScope *Parent, LVElement *Child) { + Parent->addElement(Child); + EXPECT_EQ(Child->getParent(), Parent); + EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1); +} + +// Helper function to set the initial values for a given logical element. +void ReaderTestCompare::set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber, LVElement *Type) { + Element->setName(Name); + Element->setOffset(Offset); + Element->setLineNumber(LineNumber); + Element->setType(Type); +} + +//===----------------------------------------------------------------------===// +// Create the logical elements. +void ReaderTestCompare::createElements() { + // Create scope root. + Error Err = createScopes(); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + Root = getScopesRoot(); + EXPECT_NE(Root, nullptr); + + // Create the logical types. + IntegerType = create(&LVType::setIsBase); + UnsignedType = create(&LVType::setIsBase); + GlobalType = create(&LVType::setIsBase); + LocalType = create(&LVType::setIsBase); + NestedType = create(&LVType::setIsBase); + EnumeratorOne = + create(&LVType::setIsEnumerator); + EnumeratorTwo = + create(&LVType::setIsEnumerator); + TypeDefinitionOne = + create(&LVType::setIsTypedef); + TypeDefinitionTwo = + create(&LVType::setIsTypedef); + + // Create the logical scopes. + NestedScope = + create(&LVScope::setIsLexicalBlock); + InnerScope = create(&LVScope::setIsLexicalBlock); + Aggregate = + create(&LVScope::setIsAggregate); + CompileUnit = create( + &LVScope::setIsCompileUnit); + Enumeration = create( + &LVScope::setIsEnumeration); + FunctionOne = + create(&LVScope::setIsFunction); + FunctionTwo = + create(&LVScope::setIsFunction); + Namespace = + create(&LVScope::setIsNamespace); + + // Create the logical symbols. + GlobalVariable = + create(&LVSymbol::setIsVariable); + LocalVariable = + create(&LVSymbol::setIsVariable); + ClassMember = create(&LVSymbol::setIsMember); + NestedVariable = + create(&LVSymbol::setIsVariable); + ParameterOne = + create(&LVSymbol::setIsParameter); + ParameterTwo = + create(&LVSymbol::setIsParameter); + + // Create the logical lines. + LineOne = create(&LVLine::setIsLineDebug); + LineTwo = create(&LVLine::setIsLineDebug); + LineThree = create(&LVLine::setIsLineDebug); +} + +// Reference Reader: Target Reader: +// ---------------------- ---------------------- +// Root Root +// CompileUnit CompileUnit +// IntegerType IntegerType +// UnsignedType UnsignedType +// FunctionOne FunctionOne +// ParameterOne ParameterOne +// LocalVariable --- +// LocalType LocalType +// LineOne LineOne +// NestedScope NestedScope +// NestedVariable NestedVariable +// NestedType NestedType +// LineTwo --- +// InnerScope InnerScope +// --- LineThree +// --- FunctionTwo +// --- ParameterTwo +// GlobalVariable GlobalVariable +// GlobalType GlobalType +// Namespace Namespace +// Aggregate Aggregate +// ClassMember ClassMember +// Enumeration Enumeration +// EnumeratorOne EnumeratorOne +// EnumeratorTwo EnumeratorTwo +// TypeDefinitionOne --- +// --- TypeDefinitionTwo + +// Create the logical view adding the created logical elements. +void ReaderTestCompare::addElements(bool IsReference, bool IsTarget) { + Root->setName(IsReference ? "Reference-Reader" : "Target-Reader"); + + auto Insert = [&](bool Insert, auto *Parent, auto *Child) { + if (Insert) + add(Parent, Child); + }; + + setCompileUnit(CompileUnit); + add(Root, CompileUnit); + + // Add elements to CompileUnit. + Insert(true, CompileUnit, IntegerType); + Insert(true, CompileUnit, UnsignedType); + Insert(true, CompileUnit, FunctionOne); + Insert(IsTarget, CompileUnit, FunctionTwo); + Insert(true, CompileUnit, GlobalVariable); + Insert(true, CompileUnit, GlobalType); + Insert(true, CompileUnit, Namespace); + + // Add elements to Namespace. + Insert(true, Namespace, Aggregate); + Insert(true, Namespace, Enumeration); + Insert(IsReference, Namespace, TypeDefinitionOne); + Insert(IsTarget, Namespace, TypeDefinitionTwo); + + // Add elements to FunctionOne. + Insert(true, FunctionOne, ParameterOne); + Insert(IsReference, FunctionOne, LocalVariable); + Insert(true, FunctionOne, LocalType); + Insert(true, FunctionOne, LineOne); + Insert(true, FunctionOne, NestedScope); + + // Add elements to FunctionTwo. + Insert(IsTarget, FunctionTwo, ParameterTwo); + + // Add elements to NestedScope. + Insert(true, NestedScope, NestedVariable); + Insert(true, NestedScope, NestedType); + Insert(IsReference, NestedScope, LineTwo); + Insert(true, NestedScope, InnerScope); + + // Add elements to Enumeration. + Insert(true, Enumeration, EnumeratorOne); + Insert(true, Enumeration, EnumeratorTwo); + + // Add elements to Aggregate. + Insert(true, Aggregate, ClassMember); + + Insert(IsTarget, InnerScope, LineThree); +} + +// Set initial values to logical elements. +void ReaderTestCompare::initElements() { + setFilename("LogicalElements.obj"); + + Root->setFileFormatName("FileFormat"); + + // Types. + set(IntegerType, "int", 0x1000); + set(UnsignedType, "unsigned", 0x1010); + set(GlobalType, "GlobalType", 0x1020, 1020); + set(LocalType, "LocalType", 0x1030, 1030); + set(NestedType, "NestedType", 0x1040, 1040); + + set(TypeDefinitionOne, "INTEGER", 0x1050, 1050, IntegerType); + set(TypeDefinitionTwo, "INT", 0x1060, 1060, TypeDefinitionOne); + + set(EnumeratorOne, "One", 0x1070, 1070); + EnumeratorOne->setValue("Blue"); + + set(EnumeratorTwo, "Two", 0x1080, 1080); + EnumeratorTwo->setValue("Red"); + + // Scopes. + set(Aggregate, "Class", 0x2000, 2000); + set(Enumeration, "Colors", 0x2010, 2010); + set(FunctionOne, "FunctionOne", 0x2020, 2020, GlobalType); + set(FunctionTwo, "FunctionTwo", 0x2030, 2030, GlobalType); + set(Namespace, "Namespace", 0x2040, 2040); + set(NestedScope, "", 0x2050, 2050); + set(InnerScope, "", 0x2060, 2060); + set(CompileUnit, "test.cpp", 0x2070, 2070); + + // Symbols. + set(GlobalVariable, "GlobalVariable", 0x3000, 3000); + set(LocalVariable, "LocalVariable", 0x3010, 3010, UnsignedType); + set(ClassMember, "ClassMember", 0x3020, 3020, IntegerType); + set(ParameterOne, "ParameterOne", 0x3030, 3030, UnsignedType); + set(ParameterTwo, "ParameterTwo", 0x3040, 3040, UnsignedType); + set(NestedVariable, "NestedVariable", 0x3050, 3050); + + // Lines. + set(LineOne, "", 0x4000, 4000); + set(LineTwo, "", 0x4010, 4010); + set(LineThree, "", 0x4020, 4020); +} + +// Compare the logical views. +void compareReadersViews(ReaderTestCompare *ReferenceReader, + ReaderTestCompare *TargetReader) { + LVCompare Compare(nulls()); + Error Err = Compare.execute(ReferenceReader, TargetReader); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + + // Get comparison table. + LVPassTable PassTable = Compare.getPassTable(); + ASSERT_EQ(PassTable.size(), 5u); + + LVReader *Reader; + LVElement *Element; + LVComparePass Pass; + + // Reference: Missing 'FunctionOne' + std::tie(Reader, Element, Pass) = PassTable[0]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->FunctionOne); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Reference: Missing 'TypeDefinitionOne' + std::tie(Reader, Element, Pass) = PassTable[1]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->TypeDefinitionOne); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Target: Added 'FunctionOne' + std::tie(Reader, Element, Pass) = PassTable[2]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->FunctionOne); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'FunctionTwo' + std::tie(Reader, Element, Pass) = PassTable[3]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->FunctionTwo); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'TypeDefinitionTwo' + std::tie(Reader, Element, Pass) = PassTable[4]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->TypeDefinitionTwo); + EXPECT_EQ(Pass, LVComparePass::Added); +} + +// Compare the logical elements. +void compareReadersElements(ReaderTestCompare *ReferenceReader, + ReaderTestCompare *TargetReader) { + LVCompare Compare(nulls()); + Error Err = Compare.execute(ReferenceReader, TargetReader); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + + // Get comparison table. + LVPassTable PassTable = Compare.getPassTable(); + ASSERT_EQ(PassTable.size(), 7u); + + LVReader *Reader; + LVElement *Element; + LVComparePass Pass; + + // Reference: Missing 'LocalVariable' + std::tie(Reader, Element, Pass) = PassTable[0]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->LocalVariable); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Reference: Missing 'TypeDefinitionOne' + std::tie(Reader, Element, Pass) = PassTable[1]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->TypeDefinitionOne); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Reference: Missing 'LineTwo' + std::tie(Reader, Element, Pass) = PassTable[2]; + EXPECT_EQ(Reader, ReferenceReader); + EXPECT_EQ(Element, ReferenceReader->LineTwo); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Target: Added 'FunctionTwo' + std::tie(Reader, Element, Pass) = PassTable[3]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->FunctionTwo); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'ParameterTwo' + std::tie(Reader, Element, Pass) = PassTable[4]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->ParameterTwo); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'TypeDefinitionTwo' + std::tie(Reader, Element, Pass) = PassTable[5]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->TypeDefinitionTwo); + EXPECT_EQ(Pass, LVComparePass::Added); + + // Target: Added 'LineThree' + std::tie(Reader, Element, Pass) = PassTable[6]; + EXPECT_EQ(Reader, TargetReader); + EXPECT_EQ(Element, TargetReader->LineThree); + EXPECT_EQ(Pass, LVComparePass::Added); +} + +TEST(LogicalViewTest, CompareElements) { + ScopedPrinter W(outs()); + + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setCompareLines(); + ReaderOptions.setCompareScopes(); + ReaderOptions.setCompareSymbols(); + ReaderOptions.setCompareTypes(); + + // The next set-ups are very similar. The only difference is the + // comparison type, which must be set before the readers are created. + // Views: setCompareContext() + // Elements: resetCompareContext() + { + // Compare the logical views as whole unit (--compare-context). + ReaderOptions.setCompareContext(); + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + ReaderTestCompare ReferenceReader(W); + ReferenceReader.createElements(); + ReferenceReader.addElements(/*IsReference=*/true, /*IsTarget=*/false); + ReferenceReader.initElements(); + + ReaderTestCompare TargetReader(W); + TargetReader.createElements(); + TargetReader.addElements(/*IsReference=*/false, /*IsTarget=*/true); + TargetReader.initElements(); + + compareReadersViews(&ReferenceReader, &TargetReader); + } + { + // Compare the logical elements. + ReaderOptions.resetCompareContext(); + ReaderOptions.resolveDependencies(); + options().setOptions(&ReaderOptions); + + ReaderTestCompare ReferenceReader(W); + ReferenceReader.createElements(); + ReferenceReader.addElements(/*IsReference=*/true, /*IsTarget=*/false); + ReferenceReader.initElements(); + + ReaderTestCompare TargetReader(W); + TargetReader.createElements(); + TargetReader.addElements(/*IsReference=*/false, /*IsTarget=*/true); + TargetReader.initElements(); + + compareReadersElements(&ReferenceReader, &TargetReader); + } +} + +} // namespace