diff options
Diffstat (limited to 'llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp')
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp new file mode 100644 index 000000000000..65baf52ffb44 --- /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 <tuple> + +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<const char *, unsigned, unsigned, unsigned>; +using LVCompareInfo = std::map<LVCompareItem, LVCompareEntry>; +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<unsigned>(LVCompareIndex::Header); +} +constexpr unsigned getExpected() { + return static_cast<unsigned>(LVCompareIndex::Expected); +} +constexpr unsigned getMissing() { + return static_cast<unsigned>(LVCompareIndex::Missing); +} +constexpr unsigned getAdded() { + return static_cast<unsigned>(LVCompareIndex::Added); +} + +LVCompare *CurrentComparator = nullptr; + +void zeroResults() { + // In case the same reader instance is used. + for (LVCompareInfo::reference Entry : Results) { + std::get<getExpected()>(Entry.second) = 0; + std::get<getMissing()>(Entry.second) = 0; + std::get<getAdded()>(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<getExpected()>(IterTotal->second); + // Update total for specific element kind. + ++std::get<getExpected()>(Iter->second); +} + +void updateMissingOrAdded(LVElement *Element, LVComparePass Pass) { + LVCompareInfo::iterator Iter = getResultsEntry(Element); + if (Pass == LVComparePass::Missing) { + ++std::get<getMissing()>(IterTotal->second); + ++std::get<getMissing()>(Iter->second); + } else { + ++std::get<getAdded()>(IterTotal->second); + ++std::get<getAdded()>(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<LVScope *, LVScope *>; + 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<LVScope *>(CurrentTarget), + static_cast<LVScope *>(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<getHeader()>(Entry.second), + std::get<getExpected()>(Entry.second), + std::get<getMissing()>(Entry.second), + std::get<getAdded()>(Entry.second)); + } +} + +void LVCompare::print(raw_ostream &OS) const { OS << "LVCompare\n"; } |