Files
clang-p2996/llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp
Carlos Alberto Enciso e98a4c5acb [llvm-debuginfo-analyzer] (07/09) - Compare elements
llvm-debuginfo-analyzer is a command line tool that processes debug
info contained in a binary file and produces a debug information
format agnostic “Logical View”, which is a high-level semantic
representation of the debug info, independent of the low-level
format.

The code has been divided into the following patches:

1) Interval tree
2) Driver and documentation
3) Logical elements
4) Locations and ranges
5) Select elements
6) Warning and internal options
7) Compare elements
8) ELF Reader
9) CodeView Reader

Full details:
https://discourse.llvm.org/t/llvm-dev-rfc-llvm-dva-debug-information-visual-analyzer/62570

This patch:

Compare elements
- Support for logical elements comparison. See '--compare' options.
  LVCompare

Reviewed By: psamolysov, probinson

Differential Revision: https://reviews.llvm.org/D125782
2022-10-24 08:27:06 +01:00

429 lines
16 KiB
C++

//===-- 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"; }