aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp')
-rw-r--r--llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp428
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"; }