diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-02-11 12:38:11 +0000 |
commit | e3b557809604d036af6e00c60f012c2025b59a5e (patch) | |
tree | 8a11ba2269a3b669601e2fd41145b174008f4da8 /llvm/lib/DebugInfo/LogicalView/Core | |
parent | 08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff) |
Diffstat (limited to 'llvm/lib/DebugInfo/LogicalView/Core')
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVCompare.cpp | 428 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp | 530 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp | 222 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp | 675 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp | 169 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp | 579 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp | 158 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp | 304 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp | 2100 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp | 113 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp | 56 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp | 449 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp | 524 |
13 files changed, 6307 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"; } diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp new file mode 100644 index 000000000000..a320752befc4 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp @@ -0,0 +1,530 @@ +//===-- LVElement.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 LVElement class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVElement.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" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Element" + +LVElementDispatch LVElement::Dispatch = { + {LVElementKind::Discarded, &LVElement::getIsDiscarded}, + {LVElementKind::Global, &LVElement::getIsGlobalReference}, + {LVElementKind::Optimized, &LVElement::getIsOptimized}}; + +LVType *LVElement::getTypeAsType() const { + return ElementType && ElementType->getIsType() + ? static_cast<LVType *>(ElementType) + : nullptr; +} + +LVScope *LVElement::getTypeAsScope() const { + return ElementType && ElementType->getIsScope() + ? static_cast<LVScope *>(ElementType) + : nullptr; +} + +// Set the element type. +void LVElement::setGenericType(LVElement *Element) { + if (!Element->isTemplateParam()) { + setType(Element); + return; + } + // For template parameters, the instance type can be a type or a scope. + if (options().getAttributeArgument()) { + if (Element->getIsKindType()) + setType(Element->getTypeAsType()); + else if (Element->getIsKindScope()) + setType(Element->getTypeAsScope()); + } else + setType(Element); +} + +// Discriminator as string. +std::string LVElement::discriminatorAsString() const { + uint32_t Discriminator = getDiscriminator(); + std::string String; + raw_string_ostream Stream(String); + if (Discriminator && options().getAttributeDiscriminator()) + Stream << "," << Discriminator; + return String; +} + +// Get the type as a string. +StringRef LVElement::typeAsString() const { + return getHasType() ? getTypeName() : typeVoid(); +} + +// Get name for element type. +StringRef LVElement::getTypeName() const { + return ElementType ? ElementType->getName() : StringRef(); +} + +static size_t getStringIndex(StringRef Name) { + // Convert the name to Unified format ('\' have been converted into '/'). + std::string Pathname(transformPath(Name)); + + // Depending on the --attribute=filename and --attribute=pathname command + // line options, use the basename or the full pathname as the name. + if (!options().getAttributePathname()) { + // Get the basename by ignoring any prefix up to the last slash ('/'). + StringRef Basename = Pathname; + size_t Pos = Basename.rfind('/'); + if (Pos != std::string::npos) + Basename = Basename.substr(Pos + 1); + return getStringPool().getIndex(Basename); + } + + return getStringPool().getIndex(Pathname); +} + +void LVElement::setName(StringRef ElementName) { + // In the case of Root or Compile Unit, get index for the flatted out name. + NameIndex = getTransformName() ? getStringIndex(ElementName) + : getStringPool().getIndex(ElementName); +} + +void LVElement::setFilename(StringRef Filename) { + // Get index for the flattened out filename. + FilenameIndex = getStringIndex(Filename); +} + +// Return the string representation of a DIE offset. +std::string LVElement::typeOffsetAsString() const { + if (options().getAttributeOffset()) { + LVElement *Element = getType(); + return hexSquareString(Element ? Element->getOffset() : 0); + } + return {}; +} + +StringRef LVElement::accessibilityString(uint32_t Access) const { + uint32_t Value = getAccessibilityCode(); + switch (Value ? Value : Access) { + case dwarf::DW_ACCESS_public: + return "public"; + case dwarf::DW_ACCESS_protected: + return "protected"; + case dwarf::DW_ACCESS_private: + return "private"; + default: + return StringRef(); + } +} + +StringRef LVElement::externalString() const { + return getIsExternal() ? "extern" : StringRef(); +} + +StringRef LVElement::inlineCodeString(uint32_t Code) const { + uint32_t Value = getInlineCode(); + switch (Value ? Value : Code) { + case dwarf::DW_INL_not_inlined: + return "not_inlined"; + case dwarf::DW_INL_inlined: + return "inlined"; + case dwarf::DW_INL_declared_not_inlined: + return "declared_not_inlined"; + case dwarf::DW_INL_declared_inlined: + return "declared_inlined"; + default: + return StringRef(); + } +} + +StringRef LVElement::virtualityString(uint32_t Virtuality) const { + uint32_t Value = getVirtualityCode(); + switch (Value ? Value : Virtuality) { + case dwarf::DW_VIRTUALITY_none: + return StringRef(); + case dwarf::DW_VIRTUALITY_virtual: + return "virtual"; + case dwarf::DW_VIRTUALITY_pure_virtual: + return "pure virtual"; + default: + return StringRef(); + } +} + +void LVElement::resolve() { + if (getIsResolved()) + return; + setIsResolved(); + + resolveReferences(); + resolveParents(); + resolveExtra(); + resolveName(); +} + +// Set File/Line using the specification element. +void LVElement::setFileLine(LVElement *Specification) { + // In the case of inlined functions, the correct scope must be associated + // with the file and line information of the outline version. + if (!isLined()) { + setLineNumber(Specification->getLineNumber()); + setIsLineFromReference(); + } + if (!isFiled()) { + setFilenameIndex(Specification->getFilenameIndex()); + setIsFileFromReference(); + } +} + +void LVElement::resolveName() { + // Set the qualified name if requested. + if (options().getAttributeQualified()) + resolveQualifiedName(); + + setIsResolvedName(); +} + +// Resolve any parents. +void LVElement::resolveParents() { + if (isRoot() || isCompileUnit()) + return; + + LVScope *Parent = getParentScope(); + if (Parent && !Parent->getIsCompileUnit()) + Parent->resolve(); +} + +// Generate a name for unnamed elements. +void LVElement::generateName(std::string &Prefix) const { + LVScope *Scope = getParentScope(); + if (!Scope) + return; + + // Use its parent name and any line information. + Prefix.append(std::string(Scope->getName())); + Prefix.append("::"); + Prefix.append(isLined() ? lineNumberAsString(/*ShowZero=*/true) : "?"); + + // Remove any whitespaces. + Prefix.erase(std::remove_if(Prefix.begin(), Prefix.end(), ::isspace), + Prefix.end()); +} + +// Generate a name for unnamed elements. +void LVElement::generateName() { + setIsAnonymous(); + std::string Name; + generateName(Name); + setName(Name); + setIsGeneratedName(); +} + +void LVElement::updateLevel(LVScope *Parent, bool Moved) { + setLevel(Parent->getLevel() + 1); + if (Moved) + setHasMoved(); +} + +// Generate the full name for the element, to include special qualifiers. +void LVElement::resolveFullname(LVElement *BaseType, StringRef Name) { + // For the following sample code, + // void *p; + // some compilers do not generate an attribute for the associated type: + // DW_TAG_variable + // DW_AT_name 'p' + // DW_AT_type $1 + // ... + // $1: DW_TAG_pointer_type + // ... + // For those cases, generate the implicit 'void' type. + StringRef BaseTypename = BaseType ? BaseType->getName() : emptyString(); + bool GetBaseTypename = false; + bool UseBaseTypename = true; + bool UseNameText = true; + + switch (getTag()) { + case dwarf::DW_TAG_pointer_type: // "*"; + if (!BaseType) + BaseTypename = typeVoid(); + break; + case dwarf::DW_TAG_const_type: // "const" + case dwarf::DW_TAG_ptr_to_member_type: // "*" + case dwarf::DW_TAG_rvalue_reference_type: // "&&" + case dwarf::DW_TAG_reference_type: // "&" + case dwarf::DW_TAG_restrict_type: // "restrict" + case dwarf::DW_TAG_volatile_type: // "volatile" + case dwarf::DW_TAG_unaligned: // "unaligned" + break; + case dwarf::DW_TAG_base_type: + case dwarf::DW_TAG_compile_unit: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_enumerator: + case dwarf::DW_TAG_namespace: + case dwarf::DW_TAG_skeleton_unit: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_unspecified_type: + case dwarf::DW_TAG_GNU_template_parameter_pack: + GetBaseTypename = true; + break; + case dwarf::DW_TAG_array_type: + case dwarf::DW_TAG_call_site: + case dwarf::DW_TAG_entry_point: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_GNU_call_site: + case dwarf::DW_TAG_imported_module: + case dwarf::DW_TAG_imported_declaration: + case dwarf::DW_TAG_inlined_subroutine: + case dwarf::DW_TAG_label: + case dwarf::DW_TAG_subprogram: + case dwarf::DW_TAG_subrange_type: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_typedef: + GetBaseTypename = true; + UseBaseTypename = false; + break; + case dwarf::DW_TAG_template_type_parameter: + case dwarf::DW_TAG_template_value_parameter: + UseBaseTypename = false; + break; + case dwarf::DW_TAG_GNU_template_template_param: + break; + case dwarf::DW_TAG_catch_block: + case dwarf::DW_TAG_lexical_block: + case dwarf::DW_TAG_try_block: + UseNameText = false; + break; + default: + llvm_unreachable("Invalid type."); + return; + break; + } + + // Overwrite if no given value. 'Name' is empty when resolving for scopes + // and symbols. In the case of types, it represents the type base name. + if (Name.empty() && GetBaseTypename) + Name = getName(); + + // Concatenate the elements to get the full type name. + // Type will be: base_parent + pre + base + parent + post. + std::string Fullname; + + if (UseNameText && Name.size()) + Fullname.append(std::string(Name)); + if (UseBaseTypename && BaseTypename.size()) { + if (UseNameText && Name.size()) + Fullname.append(" "); + Fullname.append(std::string(BaseTypename)); + } + + // For a better and consistent layout, check if the generated name + // contains double space sequences. + assert((Fullname.find(" ", 0) == std::string::npos) && + "Extra double spaces in name."); + + LLVM_DEBUG({ dbgs() << "Fullname = '" << Fullname << "'\n"; }); + setName(Fullname); +} + +void LVElement::setFile(LVElement *Reference) { + if (!options().getAttributeAnySource()) + return; + + // At this point, any existing reference to another element, have been + // resolved and the file ID extracted from the DI entry. + if (Reference) + setFileLine(Reference); + + // The file information is used to show the source file for any element + // and display any new source file in relation to its parent element. + // a) Elements that are not inlined. + // - We record the DW_AT_decl_line and DW_AT_decl_file. + // b) Elements that are inlined. + // - We record the DW_AT_decl_line and DW_AT_decl_file. + // - We record the DW_AT_call_line and DW_AT_call_file. + // For both cases, we use the DW_AT_decl_file value to detect any changes + // in the source filename containing the element. Changes on this value + // indicates that the element being printed is not contained in the + // previous printed filename. + + // The source files are indexed starting at 0, but DW_AT_decl_file defines + // that 0 means no file; a value of 1 means the 0th entry. + size_t Index = 0; + + // An element with no source file information will use the reference + // attribute (DW_AT_specification, DW_AT_abstract_origin, DW_AT_extension) + // to update its information. + if (getIsFileFromReference() && Reference) { + Index = Reference->getFilenameIndex(); + if (Reference->getInvalidFilename()) + setInvalidFilename(); + setFilenameIndex(Index); + return; + } + + // The source files are indexed starting at 0, but DW_AT_decl_file + // defines that 0 means no file; a value of 1 means the 0th entry. + Index = getFilenameIndex(); + if (Index) { + StringRef Filename = getReader().getFilename(this, Index); + Filename.size() ? setFilename(Filename) : setInvalidFilename(); + } +} + +LVScope *LVElement::traverseParents(LVScopeGetFunction GetFunction) const { + LVScope *Parent = getParentScope(); + while (Parent && !(Parent->*GetFunction)()) + Parent = Parent->getParentScope(); + return Parent; +} + +LVScope *LVElement::getFunctionParent() const { + return traverseParents(&LVScope::getIsFunction); +} + +LVScope *LVElement::getCompileUnitParent() const { + return traverseParents(&LVScope::getIsCompileUnit); +} + +// Resolve the qualified name to include the parent hierarchy names. +void LVElement::resolveQualifiedName() { + if (!getIsReferencedType() || isBase() || getQualifiedResolved() || + !getIncludeInPrint()) + return; + + std::string Name; + + // Get the qualified name, excluding the Compile Unit. + LVScope *Parent = getParentScope(); + if (Parent && !Parent->getIsRoot()) { + while (Parent && !Parent->getIsCompileUnit()) { + Name.insert(0, "::"); + if (Parent->isNamed()) + Name.insert(0, std::string(Parent->getName())); + else { + std::string Temp; + Parent->generateName(Temp); + Name.insert(0, Temp); + } + Parent = Parent->getParentScope(); + } + } + + if (Name.size()) { + setQualifiedName(Name); + setQualifiedResolved(); + } + LLVM_DEBUG({ + dbgs() << "Offset: " << hexSquareString(getOffset()) + << ", Kind: " << formattedKind(kind()) + << ", Name: " << formattedName(getName()) + << ", QualifiedName: " << formattedName(Name) << "\n"; + }); +} + +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() && + getFilenameIndex()) { + + // Check if there is a change in the File ID sequence. + size_t Index = getFilenameIndex(); + if (options().changeFilenameIndex(Index)) { + // Just to keep a nice layout. + OS << "\n"; + printAttributes(OS, /*Full=*/false); + + OS << " {Source} "; + if (getInvalidFilename()) + OS << format("[0x%08x]\n", Index); + else + OS << formattedName(getPathname()) << "\n"; + } + } +} + +void LVElement::printReference(raw_ostream &OS, bool Full, + LVElement *Parent) const { + if (options().getPrintFormatting() && options().getAttributeReference()) + printAttributes(OS, Full, "{Reference} ", Parent, + referenceAsString(getLineNumber(), /*Spaces=*/false), + /*UseQuotes=*/false, /*PrintRef=*/true); +} + +void LVElement::printLinkageName(raw_ostream &OS, bool Full, + LVElement *Parent) const { + if (options().getPrintFormatting() && options().getAttributeLinkage()) { + printAttributes(OS, Full, "{Linkage} ", Parent, getLinkageName(), + /*UseQuotes=*/true, /*PrintRef=*/false); + } +} + +void LVElement::printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent, + LVScope *Scope) const { + if (options().getPrintFormatting() && options().getAttributeLinkage()) { + LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope); + std::string Text = (Twine(" 0x") + Twine::utohexstr(SectionIndex) + + Twine(" '") + Twine(getLinkageName()) + Twine("'")) + .str(); + printAttributes(OS, Full, "{Linkage} ", Parent, Text, + /*UseQuotes=*/false, /*PrintRef=*/false); + } +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp new file mode 100644 index 000000000000..c3810d282abc --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp @@ -0,0 +1,222 @@ +//===-- LVLine.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 LVLine class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Line" + +namespace { +const char *const KindBasicBlock = "BasicBlock"; +const char *const KindDiscriminator = "Discriminator"; +const char *const KindEndSequence = "EndSequence"; +const char *const KindEpilogueBegin = "EpilogueBegin"; +const char *const KindLineDebug = "Line"; +const char *const KindLineSource = "Code"; +const char *const KindNewStatement = "NewStatement"; +const char *const KindPrologueEnd = "PrologueEnd"; +const char *const KindUndefined = "Undefined"; +const char *const KindAlwaysStepInto = "AlwaysStepInto"; // CodeView +const char *const KindNeverStepInto = "NeverStepInto"; // CodeView +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Logical line. +//===----------------------------------------------------------------------===// +// Return a string representation for the line kind. +const char *LVLine::kind() const { + const char *Kind = KindUndefined; + if (getIsLineDebug()) + Kind = KindLineDebug; + else if (getIsLineAssembler()) + Kind = KindLineSource; + return Kind; +} + +LVLineDispatch LVLine::Dispatch = { + {LVLineKind::IsBasicBlock, &LVLine::getIsBasicBlock}, + {LVLineKind::IsDiscriminator, &LVLine::getIsDiscriminator}, + {LVLineKind::IsEndSequence, &LVLine::getIsEndSequence}, + {LVLineKind::IsLineDebug, &LVLine::getIsLineDebug}, + {LVLineKind::IsLineAssembler, &LVLine::getIsLineAssembler}, + {LVLineKind::IsNewStatement, &LVLine::getIsNewStatement}, + {LVLineKind::IsEpilogueBegin, &LVLine::getIsEpilogueBegin}, + {LVLineKind::IsPrologueEnd, &LVLine::getIsPrologueEnd}, + {LVLineKind::IsAlwaysStepInto, &LVLine::getIsAlwaysStepInto}, + {LVLineKind::IsNeverStepInto, &LVLine::getIsNeverStepInto}}; + +// String used as padding for printing elements with no line number. +std::string LVLine::noLineAsString(bool ShowZero) const { + if (options().getInternalNone()) + return LVObject::noLineAsString(ShowZero); + return (ShowZero || options().getAttributeZero()) ? (" 0 ") + : (" - "); +} + +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(); + LVElement::print(OS, Full); + printExtra(OS, Full); + } +} + +//===----------------------------------------------------------------------===// +// DWARF line record. +//===----------------------------------------------------------------------===// +std::string LVLineDebug::statesInfo(bool Formatted) const { + // Returns the DWARF extra qualifiers. + std::string String; + raw_string_ostream Stream(String); + + std::string Separator = Formatted ? " " : ""; + if (getIsNewStatement()) { + Stream << Separator << "{" << KindNewStatement << "}"; + Separator = " "; + } + if (getIsDiscriminator()) { + Stream << Separator << "{" << KindDiscriminator << "}"; + Separator = " "; + } + if (getIsBasicBlock()) { + Stream << Separator << "{" << KindBasicBlock << "}"; + Separator = " "; + } + if (getIsEndSequence()) { + Stream << Separator << "{" << KindEndSequence << "}"; + Separator = " "; + } + if (getIsEpilogueBegin()) { + Stream << Separator << "{" << KindEpilogueBegin << "}"; + Separator = " "; + } + if (getIsPrologueEnd()) { + Stream << Separator << "{" << KindPrologueEnd << "}"; + Separator = " "; + } + if (getIsAlwaysStepInto()) { + Stream << Separator << "{" << KindAlwaysStepInto << "}"; + Separator = " "; + } + if (getIsNeverStepInto()) { + Stream << Separator << "{" << KindNeverStepInto << "}"; + Separator = " "; + } + + 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()); + + if (options().getAttributeQualifier()) { + // The qualifier includes the states information and the source filename + // that contains the line element. + OS << statesInfo(/*Formatted=*/true); + OS << " " << formattedName(getPathname()); + } + OS << "\n"; +} + +//===----------------------------------------------------------------------===// +// 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()); + OS << "\n"; +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp new file mode 100644 index 000000000000..115b903c6c7f --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp @@ -0,0 +1,675 @@ +//===-- LVLocation.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 LVOperation and LVLocation classes. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Location" + +void LVOperation::print(raw_ostream &OS, bool Full) const {} + +// Identify the most common type of operations and print them using a high +// level format, trying to isolate the DWARF complexity. +std::string LVOperation::getOperandsDWARFInfo() { + std::string String; + raw_string_ostream Stream(String); + + auto PrintRegisterInfo = [&](LVSmall Code) { + //----------------------------------------------------------------------- + // 2.5.1.1 Literal encodings. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_lit0 <= Code && Code <= dwarf::DW_OP_lit31) { + Stream << format("lit%d", Code - dwarf::DW_OP_lit0); + return; + } + + //----------------------------------------------------------------------- + // 2.5.1.2 Register values. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_breg0 <= Code && Code <= dwarf::DW_OP_breg31) { + std::string RegisterName(getReader().getRegisterName(Code, Operands)); + Stream << format("breg%d+%d%s", Code - dwarf::DW_OP_breg0, Operands[0], + RegisterName.c_str()); + return; + } + + //----------------------------------------------------------------------- + // 2.6.1.1.3 Register location descriptions. + //----------------------------------------------------------------------- + if (dwarf::DW_OP_reg0 <= Code && Code <= dwarf::DW_OP_reg31) { + std::string RegisterName(getReader().getRegisterName(Code, Operands)); + Stream << format("reg%d%s", Code - dwarf::DW_OP_reg0, + RegisterName.c_str()); + return; + } + + Stream << format("#0x%02x ", Code) << hexString(Operands[0]) << " " + << hexString(Operands[1]) << "#"; + }; + + switch (Opcode) { + //------------------------------------------------------------------------- + // 2.5.1.1 Literal encodings. + //------------------------------------------------------------------------- + case dwarf::DW_OP_addr: + Stream << "addr " << hexString(Operands[0]); + break; + case dwarf::DW_OP_constu: + case dwarf::DW_OP_const1u: + case dwarf::DW_OP_const2u: + case dwarf::DW_OP_const4u: + case dwarf::DW_OP_const8u: + Stream << "const_u " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_consts: + case dwarf::DW_OP_const1s: + case dwarf::DW_OP_const2s: + case dwarf::DW_OP_const4s: + case dwarf::DW_OP_const8s: + Stream << "const_s " << int(Operands[0]); + break; + case dwarf::DW_OP_addrx: + Stream << "addrx " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_constx: + Stream << "constx " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_const_type: + Stream << "TODO: DW_OP_const_type"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.2 Register values. + //------------------------------------------------------------------------- + case dwarf::DW_OP_fbreg: + Stream << "fbreg " << int(Operands[0]); + break; + case dwarf::DW_OP_bregx: { + std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); + Stream << format("bregx %d%s+%d", Operands[0], RegisterName.c_str(), + unsigned(Operands[1])); + break; + } + case dwarf::DW_OP_regval_type: { + std::string RegisterName(getReader().getRegisterName(Opcode, Operands)); + Stream << format("regval_type %d%s+%d", Operands[0], RegisterName.c_str(), + unsigned(Operands[1])); + break; + } + + //------------------------------------------------------------------------- + // 2.5.1.3 Stack operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_dup: + Stream << "dup"; + break; + case dwarf::DW_OP_drop: + Stream << "drop"; + break; + case dwarf::DW_OP_pick: + Stream << "pick " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_over: + Stream << "over"; + break; + case dwarf::DW_OP_swap: + Stream << "swap"; + break; + case dwarf::DW_OP_rot: + Stream << "rot"; + break; + case dwarf::DW_OP_deref: + Stream << "deref"; + break; + case dwarf::DW_OP_deref_size: + Stream << "deref_size " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_deref_type: + Stream << "deref_type " << unsigned(Operands[0]) << " DIE offset " + << hexString(Operands[1]); + break; + case dwarf::DW_OP_xderef: + Stream << "xderef"; + break; + case dwarf::DW_OP_xderef_size: + Stream << "xderef_size " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_xderef_type: + Stream << "xderef_type " << unsigned(Operands[0]) << " DIE offset " + << hexString(Operands[1]); + break; + case dwarf::DW_OP_push_object_address: + Stream << "push_object_address"; + break; + case dwarf::DW_OP_form_tls_address: + Stream << "form_tls_address " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call_frame_cfa: + Stream << "call_frame_cfa"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.4 Arithmetic and Logical Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_abs: + Stream << "abs"; + break; + case dwarf::DW_OP_and: + Stream << "and"; + break; + case dwarf::DW_OP_div: + Stream << "div"; + break; + case dwarf::DW_OP_minus: + Stream << "minus"; + break; + case dwarf::DW_OP_mod: + Stream << "mod"; + break; + case dwarf::DW_OP_mul: + Stream << "mul"; + break; + case dwarf::DW_OP_neg: + Stream << "neg"; + break; + case dwarf::DW_OP_not: + Stream << "not"; + break; + case dwarf::DW_OP_or: + Stream << "or"; + break; + case dwarf::DW_OP_plus: + Stream << "plus"; + break; + case dwarf::DW_OP_plus_uconst: + Stream << "plus_uconst " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_shl: + Stream << "shl"; + break; + case dwarf::DW_OP_shr: + Stream << "shr"; + break; + case dwarf::DW_OP_shra: + Stream << "shra"; + break; + case dwarf::DW_OP_xor: + Stream << "xor"; + break; + + //------------------------------------------------------------------------- + // 2.5.1.5 Control Flow Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_le: + Stream << "le"; + break; + case dwarf::DW_OP_ge: + Stream << "ge"; + break; + case dwarf::DW_OP_eq: + Stream << "eq"; + break; + case dwarf::DW_OP_lt: + Stream << "lt"; + break; + case dwarf::DW_OP_gt: + Stream << "gt"; + break; + case dwarf::DW_OP_ne: + Stream << "ne"; + break; + case dwarf::DW_OP_skip: + Stream << "skip " << signed(Operands[0]); + break; + case dwarf::DW_OP_bra: + Stream << "bra " << signed(Operands[0]); + break; + case dwarf::DW_OP_call2: + Stream << "call2 DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call4: + Stream << "call4 DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_call_ref: + Stream << "call_ref DIE offset " << hexString(Operands[0]); + break; + + //------------------------------------------------------------------------- + // 2.5.1.6 Type Conversions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_convert: + Stream << "convert DIE offset " << hexString(Operands[0]); + break; + case dwarf::DW_OP_reinterpret: + Stream << "reinterpret DIE offset " << hexString(Operands[0]); + break; + + //------------------------------------------------------------------------- + // 2.5.1.7 Special Operations. + //------------------------------------------------------------------------- + case dwarf::DW_OP_nop: + Stream << "nop"; + break; + case dwarf::DW_OP_entry_value: + Stream << "TODO: DW_OP_entry_value"; + break; + + //------------------------------------------------------------------------- + // 2.6.1.1.3 Register location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_regx: + Stream << "regx" << getReader().getRegisterName(Opcode, Operands); + break; + + //------------------------------------------------------------------------- + // 2.6.1.1.4 Implicit location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_stack_value: + Stream << "stack_value"; + break; + case dwarf::DW_OP_implicit_value: + Stream << "TODO: DW_OP_implicit_value"; + break; + case dwarf::DW_OP_implicit_pointer: + Stream << "implicit_pointer DIE offset " << hexString(Operands[0]) << " " + << int(Operands[1]); + break; + + //------------------------------------------------------------------------- + // 2.6.1.2 Composite location descriptions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_piece: + Stream << "piece " << int(Operands[0]); + break; + case dwarf::DW_OP_bit_piece: + Stream << "bit_piece " << int(Operands[0]) << " offset " + << int(Operands[1]); + break; + + //------------------------------------------------------------------------- + // GNU extensions. + //------------------------------------------------------------------------- + case dwarf::DW_OP_GNU_entry_value: + Stream << "gnu_entry_value "; + PrintRegisterInfo(dwarf::DW_OP_reg0); + break; + case dwarf::DW_OP_GNU_push_tls_address: + Stream << "gnu_push_tls_address " << hexString(Operands[0]); + break; + case dwarf::DW_OP_GNU_addr_index: + Stream << "gnu_addr_index " << unsigned(Operands[0]); + break; + case dwarf::DW_OP_GNU_const_index: + Stream << "gnu_const_index " << unsigned(Operands[0]); + break; + + //------------------------------------------------------------------------- + // Member location. + //------------------------------------------------------------------------- + case LVLocationMemberOffset: + Stream << "offset " << int(Operands[0]); + break; + + //------------------------------------------------------------------------- + // Missing location. + //------------------------------------------------------------------------- + case dwarf::DW_OP_hi_user: + Stream << "missing"; + break; + + //------------------------------------------------------------------------- + // Register values. + //------------------------------------------------------------------------- + default: + PrintRegisterInfo(Opcode); + break; + } + + return String; +} + +// Identify the most common type of operations and print them using a high +// level format, trying to isolate the CodeView complexity. +std::string LVOperation::getOperandsCodeViewInfo() { + std::string String; + raw_string_ostream Stream(String); + + // Get original CodeView operation code. + uint16_t OperationCode = getCodeViewOperationCode(Opcode); + + switch (OperationCode) { + // Operands: [Offset, 0]. + case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL: + Stream << "frame_pointer_rel " << int(Operands[0]); + break; + case codeview::SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + Stream << "frame_pointer_rel_full_scope " << int(Operands[0]); + break; + + // Operands: [Register, 0]. + case codeview::SymbolKind::S_DEFRANGE_REGISTER: + Stream << "register " << getReader().getRegisterName(Opcode, Operands); + break; + case codeview::SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER: + Stream << "subfield_register " + << getReader().getRegisterName(Opcode, Operands); + break; + + // Operands: [Register, Offset]. + case codeview::SymbolKind::S_DEFRANGE_REGISTER_REL: + Stream << "register_rel " << getReader().getRegisterName(Opcode, Operands) + << " offset " << int(Operands[1]); + break; + + // Operands: [Program, 0]. + case codeview::SymbolKind::S_DEFRANGE: + Stream << "frame " << int(Operands[0]); + break; + case codeview::SymbolKind::S_DEFRANGE_SUBFIELD: + Stream << "subfield " << int(Operands[0]); + break; + + default: + Stream << format("#0x%02x: ", Opcode) << hexString(Operands[0]) << " " + << hexString(Operands[1]) << "#"; + break; + } + + return String; +} + +namespace { +const char *const KindBaseClassOffset = "BaseClassOffset"; +const char *const KindBaseClassStep = "BaseClassStep"; +const char *const KindClassOffset = "ClassOffset"; +const char *const KindFixedAddress = "FixedAddress"; +const char *const KindMissingInfo = "Missing"; +const char *const KindOperation = "Operation"; +const char *const KindOperationList = "OperationList"; +const char *const KindRegister = "Register"; +const char *const KindUndefined = "Undefined"; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// DWARF location information. +//===----------------------------------------------------------------------===// +const char *LVLocation::kind() const { + const char *Kind = KindUndefined; + if (getIsBaseClassOffset()) + Kind = KindBaseClassOffset; + else if (getIsBaseClassStep()) + Kind = KindBaseClassStep; + else if (getIsClassOffset()) + Kind = KindClassOffset; + else if (getIsFixedAddress()) + Kind = KindFixedAddress; + else if (getIsGapEntry()) + Kind = KindMissingInfo; + else if (getIsOperation()) + Kind = KindOperation; + else if (getIsOperationList()) + Kind = KindOperationList; + else if (getIsRegister()) + Kind = KindRegister; + return Kind; +} + +std::string LVLocation::getIntervalInfo() const { + static const char *const Question = "?"; + std::string String; + raw_string_ostream Stream(String); + if (getIsAddressRange()) + Stream << "{Range}"; + + auto PrintLine = [&](const LVLine *Line) { + if (Line) { + std::string TheLine; + TheLine = Line->lineNumberAsStringStripped(); + Stream << TheLine.c_str(); + } else { + Stream << Question; + } + }; + + Stream << " Lines "; + PrintLine(getLowerLine()); + Stream << ":"; + PrintLine(getUpperLine()); + + if (options().getAttributeOffset()) + // Print the active range (low pc and high pc). + Stream << " [" << hexString(getLowerAddress()) << ":" + << hexString(getUpperAddress()) << "]"; + + return String; +} + +// Validate the ranges associated with the location. +bool LVLocation::validateRanges() { + // Traverse the locations and validate them against the address to line + // mapping in the current compile unit. Record those invalid ranges. + // A valid range must meet the following conditions: + // a) line(lopc) <= line(hipc) + // b) line(lopc) and line(hipc) are valid. + + if (!hasAssociatedRange()) + return true; + + LVLineRange Range = getReaderCompileUnit()->lineRange(this); + LVLine *LowLine = Range.first; + LVLine *HighLine = Range.second; + if (LowLine) + setLowerLine(LowLine); + else { + setIsInvalidLower(); + return false; + } + if (HighLine) + setUpperLine(HighLine); + else { + setIsInvalidUpper(); + return false; + } + // Check for a valid interval. + if (LowLine->getLineNumber() > HighLine->getLineNumber()) { + setIsInvalidRange(); + return false; + } + + return true; +} + +bool LVLocation::calculateCoverage(LVLocations *Locations, unsigned &Factor, + float &Percentage) { + if (!options().getAttributeCoverage() && !Locations) + return false; + + // Calculate the coverage depending on the kind of location. We have + // the simple and composed locations. + if (Locations->size() == 1) { + // Simple: fixed address, class offset, stack offset. + LVLocation *Location = Locations->front(); + // Some types of locations do not have specific kind. Now is the time + // to set those types, depending on the operation type. + Location->updateKind(); + if (Location->getIsLocationSimple()) { + Factor = 100; + Percentage = 100; + return true; + } + } + + // Composed locations. + LVAddress LowerAddress = 0; + LVAddress UpperAddress = 0; + for (const LVLocation *Location : *Locations) + // Do not include locations representing a gap. + if (!Location->getIsGapEntry()) { + LowerAddress = Location->getLowerAddress(); + UpperAddress = Location->getUpperAddress(); + Factor += (UpperAddress > LowerAddress) ? UpperAddress - LowerAddress + : LowerAddress - UpperAddress; + } + + Percentage = 0; + return false; +} + +void LVLocation::printRaw(raw_ostream &OS, bool Full) const { + // Print the active range (low pc and high pc). + OS << " [" << hexString(getLowerAddress()) << ":" + << hexString(getUpperAddress()) << "]\n"; + // Print any DWARF operations. + printRawExtra(OS, Full); +} + +void LVLocation::printInterval(raw_ostream &OS, bool Full) const { + if (hasAssociatedRange()) + OS << getIntervalInfo(); +} + +void LVLocation::print(raw_ostream &OS, bool Full) const { + if (getReader().doPrintLocation(this)) { + LVObject::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVLocation::printExtra(raw_ostream &OS, bool Full) const { + printInterval(OS, Full); + OS << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF location for a symbol. +//===----------------------------------------------------------------------===// +// Add a Location Entry. +void LVLocationSymbol::addObject(LVAddress LowPC, LVAddress HighPC, + LVUnsigned SectionOffset, + uint64_t LocDescOffset) { + setLowerAddress(LowPC); + setUpperAddress(HighPC); + + // Record the offset where the location information begins. + setOffset(LocDescOffset ? LocDescOffset : SectionOffset); + + // A -1 HighPC value, indicates no range. + if (HighPC == LVAddress(UINT64_MAX)) + setIsDiscardedRange(); + + // Update the location kind, using the DWARF attribute. + setKind(); +} + +// Add a Location Record. +void LVLocationSymbol::addObject(LVSmall Opcode, LVUnsigned Operand1, + LVUnsigned Operand2) { + if (!Entries) + Entries = new LVAutoOperations(); + Entries->emplace_back(new LVOperation(Opcode, Operand1, Operand2)); +} + +// Based on the DWARF attribute, define the location kind. +void LVLocation::setKind() { + switch (getAttr()) { + case dwarf::DW_AT_data_member_location: + setIsClassOffset(); + break; + case dwarf::DW_AT_location: + // Depending on the operand, we have a fixed address. + setIsFixedAddress(); + break; + default: + break; + } + // For those symbols with absolute location information, ignore any + // gaps in their location description; that is the case with absolute + // memory addresses and members located at specific offsets. + if (hasAssociatedRange()) + getParentSymbol()->setFillGaps(); +} + +void LVLocationSymbol::updateKind() { + // Update the location type for simple ones. + if (Entries && Entries->size() == 1) { + LVOperation *Operation = Entries->front(); + if (dwarf::DW_OP_fbreg == Operation->getOpcode()) + setIsStackOffset(); + } +} + +void LVLocationSymbol::printRawExtra(raw_ostream &OS, bool Full) const { + if (Entries) + for (const LVOperation *Operation : *Entries) + Operation->print(OS, Full); +} + +// Print location (formatted version). +void LVLocation::print(LVLocations *Locations, raw_ostream &OS, bool Full) { + if (!Locations || Locations->empty()) + return; + + // Print the symbol coverage. + if (options().getAttributeCoverage()) { + // The location entries are contained within a symbol. Get a location, + // to access basic information about indentation, parent, etc. + LVLocation *Location = Locations->front(); + LVSymbol *Symbol = Location->getParentSymbol(); + float Percentage = Symbol->getCoveragePercentage(); + + // The coverage is dependent on the kind of location. + std::string String; + raw_string_ostream Stream(String); + Stream << format("%.2f%%", Percentage); + if (!Location->getIsLocationSimple()) + Stream << format(" (%d/%d)", Symbol->getCoverageFactor(), + Symbol->getParentScope()->getCoverageFactor()); + Symbol->printAttributes(OS, Full, "{Coverage} ", Symbol, StringRef(String), + /*UseQuotes=*/false, + /*PrintRef=*/false); + } + + // Print the symbol location, including the missing entries. + if (getReader().doPrintLocation(/*Location=*/nullptr)) + for (const LVLocation *Location : *Locations) + Location->print(OS, Full); +} + +void LVLocationSymbol::printExtra(raw_ostream &OS, bool Full) const { + OS << "{Location}"; + if (getIsCallSite()) + OS << " -> CallSite"; + printInterval(OS, Full); + OS << "\n"; + + // Print location entries. + if (Full && Entries) { + bool CodeViewLocation = getParentSymbol()->getHasCodeViewLocation(); + std::stringstream Stream; + std::string Leading = ""; + for (LVOperation *Operation : *Entries) { + Stream << Leading + << (CodeViewLocation ? Operation->getOperandsCodeViewInfo() + : Operation->getOperandsDWARFInfo()); + Leading = ", "; + } + printAttributes(OS, Full, "{Entry} ", const_cast<LVLocationSymbol *>(this), + StringRef(Stream.str()), + /*UseQuotes=*/false, + /*PrintRef=*/false); + } +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp new file mode 100644 index 000000000000..75acbf3225e0 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp @@ -0,0 +1,169 @@ +//===-- LVObject.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 LVObject class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVObject.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include <iomanip> + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Object" + +#ifndef NDEBUG +uint64_t LVObject::GID = 0; +#endif + +StringRef llvm::logicalview::typeNone() { return StringRef(); } +StringRef llvm::logicalview::typeVoid() { return "void"; } +StringRef llvm::logicalview::typeInt() { return "int"; } +StringRef llvm::logicalview::typeUnknown() { return "?"; } +StringRef llvm::logicalview::emptyString() { return StringRef(); } + +// Get a string representing the indentation level. +std::string LVObject::indentAsString(LVLevel Level) const { + return std::string(Level * 2, ' '); +} + +// Get a string representing the indentation level. +std::string LVObject::indentAsString() const { + return (options().getPrintFormatting() || options().getPrintOffset()) + ? indentAsString(ScopeLevel) + : ""; +} + +// String used as padding for printing objects with no line number. +std::string LVObject::noLineAsString(bool ShowZero) const { + return std::string(8, ' '); +} + +// Get a string representation for the given number and discriminator. +std::string LVObject::lineAsString(uint32_t LineNumber, LVHalf Discriminator, + bool ShowZero) const { + // The representation is formatted as: + // a) line number (xxxxx) and discriminator (yy): 'xxxxx,yy' + // b) Only line number (xxxxx): 'xxxxx ' + // c) No line number: ' ' + std::stringstream Stream; + if (LineNumber) { + if (Discriminator && options().getAttributeDiscriminator()) + Stream << std::setw(5) << LineNumber << "," << std::left << std::setw(2) + << Discriminator; + else + Stream << std::setw(5) << LineNumber << " "; + } else + Stream << noLineAsString(ShowZero); + + if (options().getInternalNone()) + Stream.str(noLineAsString(ShowZero)); + + return Stream.str(); +} + +// Same as 'LineString' but with stripped whitespaces. +std::string LVObject::lineNumberAsStringStripped(bool ShowZero) const { + return std::string(StringRef(lineNumberAsString(ShowZero)).trim()); +} + +std::string LVObject::referenceAsString(uint32_t LineNumber, + bool Spaces) const { + std::string String; + raw_string_ostream Stream(String); + if (LineNumber) + Stream << "@" << LineNumber << (Spaces ? " " : ""); + + return String; +} + +void LVObject::setParent(LVScope *Scope) { + Parent.Scope = Scope; + setLevel(Scope->getLevel() + 1); +} +void LVObject::setParent(LVSymbol *Symbol) { + Parent.Symbol = Symbol; + 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); + return Error::success(); +} + +void LVObject::printAttributes(raw_ostream &OS, bool Full, StringRef Name, + LVObject *Parent, StringRef Value, + bool UseQuotes, bool PrintRef) const { + // The current object will be the enclosing scope, use its offset and level. + LVObject Object(*Parent); + Object.setLevel(Parent->getLevel() + 1); + Object.setLineNumber(0); + Object.printAttributes(OS, Full); + + // Print the line. + std::string TheLineNumber(Object.lineNumberAsString()); + std::string TheIndentation(Object.indentAsString()); + OS << format(" %5s %s ", TheLineNumber.c_str(), TheIndentation.c_str()); + + OS << Name; + if (PrintRef && options().getAttributeOffset()) + OS << hexSquareString(getOffset()); + if (UseQuotes) + OS << formattedName(Value) << "\n"; + else + OS << Value << "\n"; +} + +void LVObject::printAttributes(raw_ostream &OS, bool Full) const { +#ifndef NDEBUG + 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()) { + std::stringstream Stream; + Stream.str(std::string()); + Stream << "[" << std::setfill('0') << std::setw(3) << getLevel() << "]"; + std::string TheLevel(Stream.str()); + OS << TheLevel; + } + if (options().getAttributeGlobal()) + OS << (getIsGlobalReference() ? 'X' : ' '); +} + +void LVObject::print(raw_ostream &OS, bool Full) const { + printFileIndex(OS, Full); + printAttributes(OS, Full); + + // Print the line and any discriminator. + std::stringstream Stream; + Stream << " " << std::setw(5) << lineNumberAsString() << " " + << indentAsString() << " "; + OS << Stream.str(); +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp new file mode 100644 index 000000000000..265237ee21dc --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVOptions.cpp @@ -0,0 +1,579 @@ +//===-- LVOptions.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 LVOptions class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/Support/Errc.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Options" + +//===----------------------------------------------------------------------===// +// Options extracted from the command line. +//===----------------------------------------------------------------------===// +static LVOptions Options; +LVOptions *LVOptions::getOptions() { return &Options; } +void LVOptions::setOptions(LVOptions *CmdOptions) { Options = *CmdOptions; } + +void LVOptions::resolveDependencies() { + // Attributes that are classified as standard options. + auto StandardAttributes = [&]() { + // Set the 'standard' attribute to indicate its associated attributes. + setAttributeStandard(); + + setAttributeBase(); + setAttributeCoverage(); + setAttributeDirectories(); + setAttributeDiscriminator(); + setAttributeFilename(); + setAttributeFiles(); + setAttributeFormat(); + setAttributeLevel(); + setAttributeProducer(); + setAttributePublics(); + setAttributeRange(); + setAttributeReference(); + setAttributeZero(); + }; + + // Attributes that are classified as extended options. + auto ExtendedAttributes = [&]() { + // Set the 'extended' attribute to indicate its associated attributes. + setAttributeExtended(); + + setAttributeArgument(); + setAttributeDiscarded(); + setAttributeEncoded(); + setAttributeGaps(); + setAttributeGenerated(); + setAttributeGlobal(); + setAttributeInserted(); + setAttributeLinkage(); + setAttributeLocal(); + setAttributeLocation(); + setAttributeOffset(); + setAttributePathname(); + setAttributeQualified(); + setAttributeQualifier(); + setAttributeRegister(); + setAttributeSubrange(); + setAttributeSystem(); + setAttributeTypename(); + }; + + // '--Attribute=standard' settings. + if (getAttributeStandard()) + StandardAttributes(); + + // '--Attribute=extended' settings. + if (getAttributeExtended()) + ExtendedAttributes(); + + // '--Attribute=all' settings. + if (getAttributeAll()) { + StandardAttributes(); + ExtendedAttributes(); + } + + // '--attribute=pathname' supersedes '--attribute=filename'. + if (getAttributePathname()) + resetAttributeFilename(); + + // Assume '--output=text' as default + if (!getOutputText() && !getOutputJson()) + setOutputText(); + + // '--output=all' settings. + if (getOutputAll()) { + setOutputJson(); + setOutputSplit(); + setOutputText(); + } + + // A view split folder was specified. + if (getOutputFolder().length()) + setOutputSplit(); + + // Always use the full pathname with splitted output. + if (getOutputSplit()) + setAttributePathname(); + + // '--print=elements' settings. + if (getPrintElements()) { + setPrintInstructions(); + setPrintLines(); + setPrintScopes(); + setPrintSymbols(); + setPrintTypes(); + } + + // '--print=all' settings. + if (getPrintAll()) { + setPrintInstructions(); + setPrintLines(); + setPrintScopes(); + setPrintSizes(); + setPrintSymbols(); + setPrintSummary(); + setPrintTypes(); + setPrintWarnings(); + } + + // '--warning=all' settings. + if (getWarningAll()) { + setWarningCoverages(); + setWarningLines(); + setWarningLocations(); + setWarningRanges(); + } + + // '--internal=all' settings. + if (getInternalAll()) { + setInternalCmdline(); + setInternalID(); + setInternalIntegrity(); + setInternalNone(); + setInternalTag(); + } + + // '--compare=all' settings. + if (getCompareAll()) { + setCompareLines(); + setCompareScopes(); + setCompareSymbols(); + setCompareTypes(); + } + + // Compare the scopes if a request for compare symbols, types, lines. + if (getCompareLines() || getCompareSymbols() || getCompareTypes()) + setCompareScopes(); + + // Generic request for comparison. + if (getCompareScopes()) + setCompareExecute(); + + // Print any logical line (debug or instruction). + if (getPrintInstructions() || getPrintLines()) + setPrintAnyLine(); + + // Print any logical element (line, scope, symbol or type). + if (getPrintAnyLine() || getPrintScopes() || getPrintSymbols() || + getPrintTypes()) + setPrintAnyElement(); + + // Print 'sizes' or 'summary'. + if (getPrintSizes() && getPrintSummary()) + setPrintSizesSummary(); + + // Generic request for printing. + if (getPrintAll() || getPrintAnyElement() || getPrintSizesSummary() || + getPrintWarnings()) + setPrintExecute(); + + // '--reports=all' settings. + if (getReportAll()) { + setReportChildren(); + setReportList(); + setReportParents(); + setReportView(); + } + + // '--report=view' is a shortcut for '--report=parents,children'. + if (getReportView()) { + setReportChildren(); + setReportParents(); + } + + // The report will include: Parents or Children. + if (getReportParents() || getReportChildren() || getReportView()) + setReportAnyView(); + + // The report will include: List or Parents or Children. + if (getReportList() || getReportAnyView()) + setReportExecute(); + + // If a view or element comparison has been requested, the following options + // must be set, in order to get a correct compare: + // 1) Sort the CUs, to get a fast compare. + // 2) Encode template instantiations, so the names include template + // parameter information. + // 3) Include qualified types. + // 4) Include any inserted abstract references. + // 5) For added/missing elements add the '+' or '-' tags. + if (getCompareExecute()) { + resetPrintExecute(); + setComparePrint(); + setSortMode(LVSortMode::Line); + setAttributeAdded(); + setAttributeArgument(); + setAttributeEncoded(); + setAttributeInserted(); + setAttributeMissing(); + setAttributeQualified(); + } + + // Enable formatting for printing (indentation, print children). + setPrintFormatting(); + + // These attributes are dependent on the capture of location information. + if (getAttributeCoverage() || getAttributeGaps() || getAttributeRegister()) + setAttributeLocation(); + + // Location information is only relevant when printing symbols. + if (!getPrintSymbols()) { + resetAttributeCoverage(); + resetAttributeGaps(); + resetAttributeLocation(); + resetAttributeRegister(); + } + + // Quick check for printing any element source information. + if (getAttributeFilename() || getAttributePathname()) + setAttributeAnySource(); + + // Quick check for printing any location information. + if (getAttributeLocation() || getAttributeRange()) + setAttributeAnyLocation(); + + if (getAttributeRange() || getPrintAnyLine()) + setGeneralCollectRanges(); + + calculateIndentationSize(); + + // Print collected command line options. + LLVM_DEBUG({ dump(); }); +} + +void LVOptions::calculateIndentationSize() { +#ifndef NDEBUG + if (getInternalID()) { + std::string String = hexSquareString(0); + IndentationSize += String.length(); + } +#endif + if (getCompareExecute() && (getAttributeAdded() || getAttributeMissing())) + ++IndentationSize; + if (getAttributeOffset()) { + std::string String = hexSquareString(0); + IndentationSize += String.length(); + } + if (getAttributeLevel()) { + std::stringstream Stream; + Stream.str(std::string()); + Stream << "[" << std::setfill('0') << std::setw(3) << 0 << "]"; + IndentationSize += Stream.tellp(); + } + if (getAttributeGlobal()) + ++IndentationSize; +} + +// Print the current values for all the options, after the dependencies +// has been resolved. +void LVOptions::print(raw_ostream &OS) const { + // --attribute + OS << "** Attributes **\n" + << "All: " << getAttributeAll() << ", " + << "Argument: " << getAttributeArgument() << ", " + << "Base: " << getAttributeBase() << ", " + << "Coverage: " << getAttributeCoverage() << "\n" + << "Directories: " << getAttributeDirectories() << ", " + << "Discarded: " << getAttributeDiscarded() << ", " + << "Discriminator: " << getAttributeDiscriminator() << ", " + << "Encoded: " << getAttributeEncoded() << "\n" + << "Extended: " << getAttributeExtended() << ", " + << "Filename: " << getAttributeFilename() << ", " + << "Files: " << getAttributeFiles() << ", " + << "Format: " << getAttributeFormat() << "\n" + << "Gaps: " << getAttributeGaps() << ", " + << "Generated: " << getAttributeGenerated() << ", " + << "Global: " << getAttributeGlobal() << ", " + << "Inserted: " << getAttributeInserted() << "\n" + << "Level: " << getAttributeLevel() << ", " + << "Linkage: " << getAttributeLinkage() << ", " + << "Local: " << getAttributeLocal() << ", " + << "Location: " << getAttributeLocation() << "\n" + << "Offset: " << getAttributeOffset() << ", " + << "Pathname: " << getAttributePathname() << ", " + << "Producer: " << getAttributeProducer() << ", " + << "Publics: " << getAttributePublics() << "\n" + << "Qualified: " << getAttributeQualified() << ", " + << "Qualifier: " << getAttributeQualifier() << ", " + << "Range: " << getAttributeRange() << ", " + << "Reference: " << getAttributeReference() << "\n" + << "Register: " << getAttributeRegister() << ", " + << "Standard: " << getAttributeStandard() << ", " + << "Subrange: " << getAttributeSubrange() << ", " + << "System: " << getAttributeSystem() << "\n" + << "Typename: " << getAttributeTypename() << ", " + << "Underlying: " << getAttributeUnderlying() << ", " + << "Zero: " << getAttributeZero() << "\n"; + OS << "Added: " << getAttributeAdded() << ", " + << "AnyLocation: " << getAttributeAnyLocation() << ", " + << "AnySource: " << getAttributeAnySource() << ", " + << "Missing: " << getAttributeMissing() << "\n" + << "\n"; + + // --compare + OS << "** Compare **\n" + << "All: " << getCompareAll() << ", " + << "Lines: " << getCompareLines() << ", " + << "Scopes: " << getCompareScopes() << ", " + << "Symbols: " << getCompareSymbols() << ", " + << "Types: " << getCompareTypes() << "\n"; + OS << "Context: " << getCompareContext() << ", " + << "Execute: " << getCompareExecute() << ", " + << "Print: " << getComparePrint() << "\n" + << "\n"; + + // --print + OS << "** Print **\n" + << "All: " << getPrintAll() << ", " + << "Elements: " << getPrintElements() << ", " + << "Instructions: " << getPrintInstructions() << ", " + << "Lines: " << getPrintLines() << "\n" + << "Scopes: " << getPrintScopes() << ", " + << "Sizes: " << getPrintSizes() << ", " + << "Summary: " << getPrintSummary() << ", " + << "Symbols: " << getPrintSymbols() << "\n" + << "Types: " << getPrintTypes() << ", " + << "Warnings: " << getPrintWarnings() << "\n"; + OS << "AnyElemeny: " << getPrintAnyElement() << ", " + << "AnyLine: " << getPrintAnyLine() << ", " + << "Execute: " << getPrintExecute() << ", " + << "Formatting: " << getPrintFormatting() << "\n" + << "Offset: " << getPrintOffset() << ", " + << "SizesSummary: " << getPrintSizesSummary() << "\n" + << "\n"; + + // --report + OS << "** Report **\n" + << "All: " << getReportAll() << ", " + << "Children: " << getReportChildren() << ", " + << "List: " << getReportList() << ", " + << "Parents: " << getReportParents() << ", " + << "View: " << getReportView() << "\n"; + OS << "AnyView: " << getReportAnyView() << ", " + << "Execute: " << getReportExecute() << "\n" + << "\n"; + + // --select + OS << "** Select **\n" + << "IgnoreCase: " << getSelectIgnoreCase() << ", " + << "UseRegex: " << getSelectUseRegex() << ", " + << "Execute: " << getSelectExecute() << ", " + << "GenericKind: " << getSelectGenericKind() << "\n" + << "GenericPattern: " << getSelectGenericPattern() << ", " + << "OffsetPattern: " << getSelectOffsetPattern() << "\n" + << "\n"; + + // --warning + OS << "** Warning **\n" + << "All: " << getWarningAll() << ", " + << "Coverage: " << getWarningCoverages() << ", " + << "Lines: " << getWarningLines() << ", " + << "Locations: " << getWarningLocations() << ", " + << "Ranges: " << getWarningRanges() << "\n" + << "\n"; + + // --internal + OS << "** Internal **\n" + << "All: " << Options.getInternalAll() << ", " + << "Cmdline: " << Options.getInternalCmdline() << ", " + << "ID: " << Options.getInternalID() << ", " + << "Integrity: " << Options.getInternalIntegrity() << ", " + << "None: " << Options.getInternalNone() << "\n" + << "Tag: " << Options.getInternalTag() << "\n" + << "\n"; +} + +//===----------------------------------------------------------------------===// +// Logical element selection using patterns. +//===----------------------------------------------------------------------===// +LVPatterns *LVPatterns::getPatterns() { + static LVPatterns Patterns; + return &Patterns; +} + +Error LVPatterns::createMatchEntry(LVMatchInfo &Filters, StringRef Pattern, + bool IgnoreCase, bool UseRegex) { + LVMatch Match; + // Process pattern as regular expression. + if (UseRegex) { + Match.Pattern = std::string(Pattern); + if (Pattern.size()) { + Match.RE = std::make_shared<Regex>(Pattern, IgnoreCase ? Regex::IgnoreCase + : Regex::NoFlags); + std::string Error; + if (!Match.RE->isValid(Error)) + return createStringError(errc::invalid_argument, + "Error in regular expression: %s", + Error.c_str()); + + Match.Mode = LVMatchMode::Regex; + Filters.push_back(Match); + return Error::success(); + } + } + + // Process pattern as an exact string match, depending on the case. + Match.Pattern = std::string(Pattern); + if (Match.Pattern.size()) { + Match.Mode = IgnoreCase ? LVMatchMode::NoCase : LVMatchMode::Match; + Filters.push_back(Match); + } + + return Error::success(); +} + +void LVPatterns::addGenericPatterns(StringSet<> &Patterns) { + addPatterns(Patterns, GenericMatchInfo); + if (GenericMatchInfo.size()) { + options().setSelectGenericPattern(); + options().setSelectExecute(); + } +} + +void LVPatterns::addOffsetPatterns(const LVOffsetSet &Patterns) { + for (const LVOffset &Entry : Patterns) + OffsetMatchInfo.push_back(Entry); + if (OffsetMatchInfo.size()) { + options().setSelectOffsetPattern(); + options().setSelectExecute(); + } +} + +void LVPatterns::addPatterns(StringSet<> &Patterns, LVMatchInfo &Filters) { + bool IgnoreCase = options().getSelectIgnoreCase(); + bool UseRegex = options().getSelectUseRegex(); + for (const StringSet<>::value_type &Entry : Patterns) { + StringRef Pattern = Entry.first(); + if (Error Err = createMatchEntry(Filters, Pattern, IgnoreCase, UseRegex)) + consumeError(std::move(Err)); + } + + LLVM_DEBUG({ + dbgs() << "\nPattern Information:\n"; + for (LVMatch &Match : Filters) + dbgs() << "Mode: " + << (Match.Mode == LVMatchMode::Match ? "Match" : "Regex") + << " Pattern: '" << Match.Pattern << "'\n"; + }); +} + +void LVPatterns::addElement(LVElement *Element) { + // Mark any element that matches a given pattern. + Element->setIsMatched(); + options().setSelectExecute(); + if (options().getReportList()) + getReaderCompileUnit()->addMatched(Element); + if (options().getReportAnyView()) { + getReaderCompileUnit()->addMatched(Element->getIsScope() + ? static_cast<LVScope *>(Element) + : Element->getParentScope()); + // Mark element as matched. + if (!Element->getIsScope()) + Element->setHasPattern(); + } +} + +void LVPatterns::updateReportOptions() { + if (ElementRequest.size() || LineRequest.size() || ScopeRequest.size() || + SymbolRequest.size() || TypeRequest.size()) { + options().setSelectGenericKind(); + options().setSelectExecute(); + } + + // If we have selected requests and there are no specified report options, + // assume the 'details' option. + if (options().getSelectExecute() && !options().getReportExecute()) { + options().setReportExecute(); + options().setReportList(); + } +} + +// Match a general pattern. +bool LVPatterns::matchPattern(StringRef Input, const LVMatchInfo &MatchInfo) { + bool Matched = false; + // Do not match an empty 'Input'. + if (Input.empty()) + return Matched; + // Traverse all match specifications. + for (const LVMatch &Match : MatchInfo) { + switch (Match.Mode) { + case LVMatchMode::Match: + Matched = Input.equals(Match.Pattern); + break; + case LVMatchMode::NoCase: + Matched = Input.equals_insensitive(Match.Pattern); + break; + case LVMatchMode::Regex: + Matched = Match.RE->match(Input); + break; + default: + break; + } + // Return if we have a match. + if (Matched) + return true; + } + return Matched; +} + +bool LVPatterns::printElement(const LVLine *Line) const { + return (options().getPrintLines() && Line->getIsLineDebug()) || + (options().getPrintInstructions() && Line->getIsLineAssembler()); +} + +bool LVPatterns::printObject(const LVLocation *Location) const { + if (options().getAttributeAll()) + return true; + bool DoPrint = options().getAttributeAnyLocation(); + // Consider the case of filler locations. + if (DoPrint && Location && Location->getIsGapEntry()) + DoPrint = options().getAttributeGaps(); + return DoPrint; +} + +bool LVPatterns::printElement(const LVScope *Scope) const { + // A scope will be printed depending on the following rules: + // - Request to print scopes. + // - Request to print any of its children. + // - If the scope is Root or CompileUnit: + // Request to print summary, sizes or warnings. + return options().getPrintScopes() || + (options().getPrintSymbols() && Scope->getHasSymbols()) || + (options().getPrintAnyLine() && Scope->getHasLines()) || + (options().getPrintTypes() && Scope->getHasTypes()) || + ((options().getPrintSizesSummary() || options().getPrintWarnings()) && + (Scope->getIsRoot() || Scope->getIsCompileUnit())); +} + +bool LVPatterns::printElement(const LVSymbol *Symbol) const { + // Print compiler generated symbols only if command line option. + if (Symbol->getIsArtificial()) + return options().getAttributeGenerated() && options().getPrintSymbols(); + return options().getPrintSymbols(); +} + +bool LVPatterns::printElement(const LVType *Type) const { + // Print array subranges only if print types is requested. + if (Type->getIsSubrange()) + return options().getAttributeSubrange() && options().getPrintTypes(); + return options().getPrintTypes(); +} + +void LVPatterns::print(raw_ostream &OS) const { + OS << "LVPatterns\n"; + LLVM_DEBUG(dbgs() << "Print Patterns\n"); +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp new file mode 100644 index 000000000000..b6003fcb8b93 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVRange.cpp @@ -0,0 +1,158 @@ +//===-- LVRange.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 LVRange class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVRange.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLocation.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Range" + +void LVRange::startSearch() { + RangesTree.clear(); + + LLVM_DEBUG({ dbgs() << "\nRanges Tree entries:\n"; }); + + // Traverse the ranges and store them into the interval tree. + for (LVRangeEntry &RangeEntry : RangeEntries) { + LLVM_DEBUG({ + LVScope *Scope = RangeEntry.scope(); + dbgs() << "Scope: " << format_decimal(Scope->getLevel(), 5) << " " + << "Range: [" << hexValue(RangeEntry.lower()) << ":" + << hexValue(RangeEntry.upper()) << "]\n"; + }); + + RangesTree.insert(RangeEntry.lower(), RangeEntry.upper(), + RangeEntry.scope()); + } + + // Create the interval tree. + RangesTree.create(); + + LLVM_DEBUG({ + dbgs() << "\nRanges Tree:\n"; + RangesTree.print(dbgs()); + }); +} + +// Add the pair in an ascending order, with the smallest ranges at the +// start; in that way, enclosing scopes ranges are at the end of the +// list; we assume that low <= high. +void LVRange::addEntry(LVScope *Scope, LVAddress LowerAddress, + LVAddress UpperAddress) { + // We assume the low <= high. + if (LowerAddress > UpperAddress) + std::swap(LowerAddress, UpperAddress); + + // Record the lowest and highest seen addresses. + if (LowerAddress < Lower) + Lower = LowerAddress; + if (UpperAddress > Upper) + Upper = UpperAddress; + + // Just add the scope and range pair, in no particular order. + RangeEntries.emplace_back(LowerAddress, UpperAddress, Scope); +} + +void LVRange::addEntry(LVScope *Scope) { + assert(Scope && "Scope must not be nullptr"); + // Traverse the ranges and update the ranges set only if the ranges + // values are not already recorded. + if (const LVLocations *Locations = Scope->getRanges()) + for (const LVLocation *Location : *Locations) { + LVAddress LowPC = Location->getLowerAddress(); + LVAddress HighPC = Location->getUpperAddress(); + if (!hasEntry(LowPC, HighPC)) + // Add the pair of addresses. + addEntry(Scope, LowPC, HighPC); + } +} + +// Get the scope associated with the input address. +LVScope *LVRange::getEntry(LVAddress Address) const { + LLVM_DEBUG({ dbgs() << format("Searching: 0x%08x\nFound: ", Address); }); + + LVScope *Target = nullptr; + LVLevel TargetLevel = 0; + LVLevel Level = 0; + LVScope *Scope = nullptr; + for (LVRangesTree::find_iterator Iter = RangesTree.find(Address), + End = RangesTree.find_end(); + Iter != End; ++Iter) { + LLVM_DEBUG( + { dbgs() << format("[0x%08x,0x%08x] ", Iter->left(), Iter->right()); }); + Scope = Iter->value(); + Level = Scope->getLevel(); + if (Level > TargetLevel) { + TargetLevel = Level; + Target = Scope; + } + } + + LLVM_DEBUG({ dbgs() << (Scope ? "\n" : "None\n"); }); + + return Target; +} + +// Find the associated Scope for the given ranges values. +LVScope *LVRange::getEntry(LVAddress LowerAddress, + LVAddress UpperAddress) const { + for (const LVRangeEntry &RangeEntry : RangeEntries) + if (LowerAddress >= RangeEntry.lower() && UpperAddress < RangeEntry.upper()) + return RangeEntry.scope(); + return nullptr; +} + +// True if the range addresses contain the pair [LowerAddress, UpperAddress]. +bool LVRange::hasEntry(LVAddress LowerAddress, LVAddress UpperAddress) const { + for (const LVRangeEntry &RangeEntry : RangeEntries) + if (LowerAddress == RangeEntry.lower() && + UpperAddress == RangeEntry.upper()) + return true; + return false; +} + +// Sort the range elements for the whole Compile Unit. +void LVRange::sort() { + auto CompareRangeEntry = [](const LVRangeEntry &lhs, + const LVRangeEntry &rhs) -> bool { + if (lhs.lower() < rhs.lower()) + return true; + + // If the lower address is the same, use the upper address value in + // order to put first the smallest interval. + if (lhs.lower() == rhs.lower()) + return lhs.upper() < rhs.upper(); + + return false; + }; + + // Sort the ranges using low address and range size. + std::stable_sort(RangeEntries.begin(), RangeEntries.end(), CompareRangeEntry); +} + +void LVRange::print(raw_ostream &OS, bool Full) const { + size_t Indentation = 0; + for (const LVRangeEntry &RangeEntry : RangeEntries) { + LVScope *Scope = RangeEntry.scope(); + Scope->printAttributes(OS, Full); + Indentation = options().indentationSize(); + if (Indentation) + OS << " "; + OS << format("[0x%08x,0x%08x] ", RangeEntry.lower(), RangeEntry.upper()) + << formattedKind(Scope->kind()) << " " << formattedName(Scope->getName()) + << "\n"; + } + printExtra(OS, Full); +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp new file mode 100644 index 000000000000..88f66cf2093b --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -0,0 +1,304 @@ +//===-- LVReader.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 LVReader class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include <tuple> + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Reader" + +// Detect elements that are inserted more than once at different scopes, +// causing a crash on the reader destruction, as the element is already +// deleted from other scope. Helper for CodeView reader. +bool checkIntegrityScopesTree(LVScope *Root) { + using LVDuplicateEntry = std::tuple<LVElement *, LVScope *, LVScope *>; + using LVDuplicate = std::vector<LVDuplicateEntry>; + LVDuplicate Duplicate; + + using LVIntegrity = std::map<LVElement *, LVScope *>; + LVIntegrity Integrity; + + // Add the given element to the integrity map. + auto AddElement = [&](LVElement *Element, LVScope *Scope) { + LVIntegrity::iterator Iter = Integrity.find(Element); + if (Iter == Integrity.end()) + Integrity.emplace(Element, Scope); + else + // We found a duplicate. + Duplicate.emplace_back(Element, Scope, Iter->second); + }; + + // Recursively add all the elements in the scope. + std::function<void(LVScope * Parent)> TraverseScope = [&](LVScope *Parent) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + AddElement(Entry, Parent); + }; + if (const LVScopes *Scopes = Parent->getScopes()) { + for (LVScope *Scope : *Scopes) { + AddElement(Scope, Parent); + TraverseScope(Scope); + } + } + Traverse(Parent->getSymbols()); + Traverse(Parent->getTypes()); + Traverse(Parent->getLines()); + }; + + // Start traversing the scopes root and print any duplicates. + TraverseScope(Root); + bool PassIntegrity = true; + if (Duplicate.size()) { + std::stable_sort(begin(Duplicate), end(Duplicate), + [](const auto &l, const auto &r) { + return std::get<0>(l)->getID() < std::get<0>(r)->getID(); + }); + + auto PrintIndex = [](unsigned Index) { + if (Index) + dbgs() << format("%8d: ", Index); + else + dbgs() << format("%8c: ", ' '); + }; + auto PrintElement = [&](LVElement *Element, unsigned Index = 0) { + PrintIndex(Index); + std::string ElementName(Element->getName()); + dbgs() << format("%15s ID=0x%08x '%s'\n", Element->kind(), + Element->getID(), ElementName.c_str()); + }; + + std::string RootName(Root->getName()); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + dbgs() << format("Root: '%s'\nDuplicated elements: %d\n", RootName.c_str(), + Duplicate.size()); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + + unsigned Index = 0; + for (const LVDuplicateEntry &Entry : Duplicate) { + LVElement *Element; + LVScope *First; + LVScope *Second; + std::tie(Element, First, Second) = Entry; + dbgs() << formatv("\n{0}\n", fmt_repeat('-', 72)); + PrintElement(Element, ++Index); + PrintElement(First); + PrintElement(Second); + dbgs() << formatv("{0}\n", fmt_repeat('-', 72)); + } + PassIntegrity = false; + } + return PassIntegrity; +} + +//===----------------------------------------------------------------------===// +// Class to represent a split context. +//===----------------------------------------------------------------------===// +Error LVSplitContext::createSplitFolder(StringRef Where) { + // The 'location' will represent the root directory for the output created + // by the context. It will contain the different CUs files, that will be + // extracted from a single ELF. + Location = std::string(Where); + + // Add a trailing slash, if there is none. + size_t Pos = Location.find_last_of('/'); + if (Location.length() != Pos + 1) + Location.append("/"); + + // Make sure the new directory exists, creating it if necessary. + if (std::error_code EC = llvm::sys::fs::create_directories(Location)) + return createStringError(EC, "Error: could not create directory %s", + Location.c_str()); + + return Error::success(); +} + +std::error_code LVSplitContext::open(std::string ContextName, + std::string Extension, raw_ostream &OS) { + assert(OutputFile == nullptr && "OutputFile already set."); + + // Transforms '/', '\', '.', ':' into '_'. + std::string Name(flattenedFilePath(ContextName)); + Name.append(Extension); + // Add the split context location folder name. + if (!Location.empty()) + Name.insert(0, Location); + + std::error_code EC; + OutputFile = std::make_unique<ToolOutputFile>(Name, EC, sys::fs::OF_None); + if (EC) + return EC; + + // Don't remove output file. + OutputFile->keep(); + return std::error_code(); +} + +LVReader *CurrentReader = nullptr; +LVReader &LVReader::getInstance() { + if (CurrentReader) + return *CurrentReader; + outs() << "Invalid instance reader.\n"; + llvm_unreachable("Invalid instance reader."); +} +void LVReader::setInstance(LVReader *Reader) { CurrentReader = Reader; } + +Error LVReader::createSplitFolder() { + if (OutputSplit) { + // If the '--output=split' was specified, but no '--split-folder' + // option, use the input file as base for the split location. + if (options().getOutputFolder().empty()) + options().setOutputFolder(getFilename().str() + "_cus"); + + SmallString<128> SplitFolder; + SplitFolder = options().getOutputFolder(); + sys::fs::make_absolute(SplitFolder); + + // Return error if unable to create a split context location. + if (Error Err = SplitContext.createSplitFolder(SplitFolder)) + return Err; + + OS << "\nSplit View Location: '" << SplitContext.getLocation() << "'\n"; + } + + return Error::success(); +} + +// Get the filename for given object. +StringRef LVReader::getFilename(LVObject *Object, size_t Index) const { + if (CompileUnits.size()) { + // Get Compile Unit for the given object. + LVCompileUnits::const_iterator Iter = + std::prev(CompileUnits.lower_bound(Object->getOffset())); + if (Iter != CompileUnits.end()) + return Iter->second->getFilename(Index); + } + + return CompileUnit ? CompileUnit->getFilename(Index) : StringRef(); +} + +// The Reader is the module that creates the logical view using the debug +// information contained in the binary file specified in the command line. +// This is the main entry point for the Reader and performs the following +// steps: +// - Process any patterns collected from the '--select' options. +// - For each compile unit in the debug information: +// * Create the logical elements (scopes, symbols, types, lines). +// * Collect debug ranges and debug locations. +// * Move the collected logical lines to their associated scopes. +// - Once all the compile units have been processed, traverse the scopes +// tree in order to: +// * Calculate symbol coverage. +// * Detect invalid ranges and locations. +// * "resolve" the logical elements. During this pass, the names and +// file information are updated, to reflect any dependency with other +// logical elements. +Error LVReader::doLoad() { + // Set current Reader instance. + setInstance(this); + + // Before any scopes creation, process any pattern specified by the + // --select and --select-offsets options. + patterns().addGenericPatterns(options().Select.Generic); + patterns().addOffsetPatterns(options().Select.Offsets); + + // Add any specific element printing requests based on the element kind. + patterns().addRequest(options().Select.Elements); + patterns().addRequest(options().Select.Lines); + patterns().addRequest(options().Select.Scopes); + patterns().addRequest(options().Select.Symbols); + patterns().addRequest(options().Select.Types); + + // Once we have processed the requests for any particular kind of elements, + // we need to update the report options, in order to have a default value. + patterns().updateReportOptions(); + + // Delegate the scope tree creation to the specific reader. + if (Error Err = createScopes()) + return Err; + + if (options().getInternalIntegrity() && !checkIntegrityScopesTree(Root)) + return llvm::make_error<StringError>("Duplicated elements in Scopes Tree", + inconvertibleErrorCode()); + + // Calculate symbol coverage and detect invalid debug locations and ranges. + Root->processRangeInformation(); + + // As the elements can depend on elements from a different compile unit, + // information such as name and file/line source information needs to be + // updated. + Root->resolveElements(); + + sortScopes(); + return Error::success(); +} + +// Default handler for a generic reader. +Error LVReader::doPrint() { + // Set current Reader instance. + setInstance(this); + + // Check for any '--report' request. + if (options().getReportExecute()) { + // Requested details. + if (options().getReportList()) + if (Error Err = printMatchedElements(/*UseMatchedElements=*/true)) + return Err; + // Requested only children. + if (options().getReportChildren() && !options().getReportParents()) + if (Error Err = printMatchedElements(/*UseMatchedElements=*/false)) + return Err; + // Requested (parents) or (parents and children). + if (options().getReportParents() || options().getReportView()) + if (Error Err = printScopes()) + return Err; + + return Error::success(); + } + + return printScopes(); +} + +Error LVReader::printScopes() { + if (bool DoPrint = + (options().getPrintExecute() || options().getComparePrint())) { + if (Error Err = createSplitFolder()) + return Err; + + // Start printing from the root. + bool DoMatch = options().getSelectGenericPattern() || + options().getSelectGenericKind() || + options().getSelectOffsetPattern(); + return Root->doPrint(OutputSplit, DoMatch, DoPrint, OS); + } + + return Error::success(); +} + +Error LVReader::printMatchedElements(bool UseMatchedElements) { + if (Error Err = createSplitFolder()) + return Err; + + return Root->doPrintMatches(OutputSplit, OS, UseMatchedElements); +} + +void LVReader::print(raw_ostream &OS) const { + OS << "LVReader\n"; + LLVM_DEBUG(dbgs() << "PrintReader\n"); +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp new file mode 100644 index 000000000000..fb503f3d3e7e --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -0,0 +1,2100 @@ +//===-- LVScope.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 LVScope class. +// +//===----------------------------------------------------------------------===// + +#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" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Scope" + +namespace { +const char *const KindArray = "Array"; +const char *const KindBlock = "Block"; +const char *const KindCallSite = "CallSite"; +const char *const KindClass = "Class"; +const char *const KindCompileUnit = "CompileUnit"; +const char *const KindEnumeration = "Enumeration"; +const char *const KindFile = "File"; +const char *const KindFunction = "Function"; +const char *const KindInlinedFunction = "InlinedFunction"; +const char *const KindNamespace = "Namespace"; +const char *const KindStruct = "Struct"; +const char *const KindTemplateAlias = "TemplateAlias"; +const char *const KindTemplatePack = "TemplatePack"; +const char *const KindUndefined = "Undefined"; +const char *const KindUnion = "Union"; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// DWARF lexical block, such as: namespace, function, compile unit, module, etc. +//===----------------------------------------------------------------------===// +LVScope::~LVScope() { + delete Types; + delete Symbols; + delete Scopes; + delete Lines; + delete Ranges; + delete Children; +} + +// Return a string representation for the scope kind. +const char *LVScope::kind() const { + const char *Kind = KindUndefined; + if (getIsArray()) + Kind = KindArray; + else if (getIsBlock()) + Kind = KindBlock; + else if (getIsCallSite()) + Kind = KindCallSite; + else if (getIsCompileUnit()) + Kind = KindCompileUnit; + else if (getIsEnumeration()) + Kind = KindEnumeration; + else if (getIsInlinedFunction()) + Kind = KindInlinedFunction; + else if (getIsNamespace()) + Kind = KindNamespace; + else if (getIsTemplatePack()) + Kind = KindTemplatePack; + else if (getIsRoot()) + Kind = KindFile; + else if (getIsTemplateAlias()) + Kind = KindTemplateAlias; + else if (getIsClass()) + Kind = KindClass; + else if (getIsFunction()) + Kind = KindFunction; + else if (getIsStructure()) + Kind = KindStruct; + else if (getIsUnion()) + Kind = KindUnion; + return Kind; +} + +LVScopeDispatch LVScope::Dispatch = { + {LVScopeKind::IsAggregate, &LVScope::getIsAggregate}, + {LVScopeKind::IsArray, &LVScope::getIsArray}, + {LVScopeKind::IsBlock, &LVScope::getIsBlock}, + {LVScopeKind::IsCallSite, &LVScope::getIsCallSite}, + {LVScopeKind::IsCatchBlock, &LVScope::getIsCatchBlock}, + {LVScopeKind::IsClass, &LVScope::getIsClass}, + {LVScopeKind::IsCompileUnit, &LVScope::getIsCompileUnit}, + {LVScopeKind::IsEntryPoint, &LVScope::getIsEntryPoint}, + {LVScopeKind::IsEnumeration, &LVScope::getIsEnumeration}, + {LVScopeKind::IsFunction, &LVScope::getIsFunction}, + {LVScopeKind::IsFunctionType, &LVScope::getIsFunctionType}, + {LVScopeKind::IsInlinedFunction, &LVScope::getIsInlinedFunction}, + {LVScopeKind::IsLabel, &LVScope::getIsLabel}, + {LVScopeKind::IsLexicalBlock, &LVScope::getIsLexicalBlock}, + {LVScopeKind::IsNamespace, &LVScope::getIsNamespace}, + {LVScopeKind::IsRoot, &LVScope::getIsRoot}, + {LVScopeKind::IsStructure, &LVScope::getIsStructure}, + {LVScopeKind::IsTemplate, &LVScope::getIsTemplate}, + {LVScopeKind::IsTemplateAlias, &LVScope::getIsTemplateAlias}, + {LVScopeKind::IsTemplatePack, &LVScope::getIsTemplatePack}, + {LVScopeKind::IsTryBlock, &LVScope::getIsTryBlock}, + {LVScopeKind::IsUnion, &LVScope::getIsUnion}}; + +void LVScope::addToChildren(LVElement *Element) { + if (!Children) + Children = new LVElements(); + Children->push_back(Element); +} + +void LVScope::addElement(LVElement *Element) { + assert(Element && "Invalid element."); + if (Element->getIsType()) + addElement(static_cast<LVType *>(Element)); + else if (Element->getIsScope()) + addElement(static_cast<LVScope *>(Element)); + else if (Element->getIsSymbol()) + addElement(static_cast<LVSymbol *>(Element)); + else if (Element->getIsLine()) + addElement(static_cast<LVLine *>(Element)); + else + llvm_unreachable("Invalid Element."); +} + +// Adds the line info item to the ones stored in the scope. +void LVScope::addElement(LVLine *Line) { + assert(Line && "Invalid line."); + assert(!Line->getParent() && "Line already inserted"); + if (!Lines) + Lines = new LVAutoLines(); + + // Add it to parent. + Lines->push_back(Line); + Line->setParent(this); + + // Notify the reader about the new element being added. + getReaderCompileUnit()->addedElement(Line); + + // All logical elements added to the children, are sorted by any of the + // following criterias: offset, name, line number, kind. + // Do not add the line records to the children, as they represent the + // logical view for the text section and any sorting will not preserve + // the original sequence. + + // Indicate that this tree branch has lines. + traverseParents(&LVScope::getHasLines, &LVScope::setHasLines); +} + +// Add a location. +void LVScope::addObject(LVLocation *Location) { + assert(Location && "Invalid location."); + assert(!Location->getParent() && "Location already inserted"); + if (!Ranges) + Ranges = new LVAutoLocations(); + + // Add it to parent. + Location->setParent(this); + Location->setOffset(getOffset()); + + Ranges->push_back(Location); + setHasRanges(); +} + +// Adds the scope to the child scopes and sets the parent in the child. +void LVScope::addElement(LVScope *Scope) { + assert(Scope && "Invalid scope."); + assert(!Scope->getParent() && "Scope already inserted"); + if (!Scopes) + Scopes = new LVAutoScopes(); + + // Add it to parent. + Scopes->push_back(Scope); + addToChildren(Scope); + Scope->setParent(this); + + // Notify the reader about the new element being added. + getReaderCompileUnit()->addedElement(Scope); + + // If the element is a global reference, mark its parent as having global + // references; that information is used, to print only those branches + // with global references. + if (Scope->getIsGlobalReference()) + traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has scopes. + traverseParents(&LVScope::getHasScopes, &LVScope::setHasScopes); +} + +// Adds a symbol to the ones stored in the scope. +void LVScope::addElement(LVSymbol *Symbol) { + assert(Symbol && "Invalid symbol."); + assert(!Symbol->getParent() && "Symbol already inserted"); + if (!Symbols) + Symbols = new LVAutoSymbols(); + + // Add it to parent. + Symbols->push_back(Symbol); + addToChildren(Symbol); + Symbol->setParent(this); + + // Notify the reader about the new element being added. + getReaderCompileUnit()->addedElement(Symbol); + + // If the element is a global reference, mark its parent as having global + // references; that information is used, to print only those branches + // with global references. + if (Symbol->getIsGlobalReference()) + traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has symbols. + traverseParents(&LVScope::getHasSymbols, &LVScope::setHasSymbols); +} + +// Adds a type to the ones stored in the scope. +void LVScope::addElement(LVType *Type) { + assert(Type && "Invalid type."); + assert(!Type->getParent() && "Type already inserted"); + if (!Types) + Types = new LVAutoTypes(); + + // Add it to parent. + Types->push_back(Type); + addToChildren(Type); + Type->setParent(this); + + // Notify the reader about the new element being added. + getReaderCompileUnit()->addedElement(Type); + + // If the element is a global reference, mark its parent as having global + // references; that information is used, to print only those branches + // with global references. + if (Type->getIsGlobalReference()) + traverseParents(&LVScope::getHasGlobals, &LVScope::setHasGlobals); + else + traverseParents(&LVScope::getHasLocals, &LVScope::setHasLocals); + + // Indicate that this tree branch has types. + traverseParents(&LVScope::getHasTypes, &LVScope::setHasTypes); +} + +// Add a pair of ranges. +void LVScope::addObject(LVAddress LowerAddress, LVAddress UpperAddress) { + // Pack the ranges into a Location object. + LVLocation *Location = new LVLocation(); + Location->setLowerAddress(LowerAddress); + Location->setUpperAddress(UpperAddress); + Location->setIsAddressRange(); + + addObject(Location); +} + +bool LVScope::removeElement(LVElement *Element) { + auto Predicate = [Element](LVElement *Item) -> bool { + return Item == Element; + }; + auto RemoveElement = [Element, Predicate](auto &Container) -> bool { + auto Iter = std::remove_if(Container->begin(), Container->end(), Predicate); + if (Iter != Container->end()) { + Container->erase(Iter, Container->end()); + Element->resetParent(); + return true; + } + return false; + }; + + // As 'children' contains only (scopes, symbols and types), check if the + // element we are deleting is a line. + if (Element->getIsLine()) + return RemoveElement(Lines); + + if (RemoveElement(Children)) { + if (Element->getIsSymbol()) + return RemoveElement(Symbols); + if (Element->getIsType()) + return RemoveElement(Types); + if (Element->getIsScope()) + return RemoveElement(Scopes); + llvm_unreachable("Invalid element."); + } + + return false; +} + +void LVScope::addMissingElements(LVScope *Reference) { + setAddedMissing(); + if (!Reference) + return; + + // Get abstract symbols for the given scope reference. + const LVSymbols *ReferenceSymbols = Reference->getSymbols(); + if (!ReferenceSymbols) + return; + + LVSymbols References; + References.append(ReferenceSymbols->begin(), ReferenceSymbols->end()); + + auto RemoveSymbol = [&](LVSymbols &Symbols, LVSymbol *Symbol) { + LVSymbols::iterator Iter = std::remove_if( + Symbols.begin(), Symbols.end(), + [Symbol](LVSymbol *Item) -> bool { return Item == Symbol; }); + if (Iter != Symbols.end()) + Symbols.erase(Iter, Symbols.end()); + }; + + // Erase abstract symbols already in this scope from the collection of + // symbols in the referenced scope. + if (getSymbols()) + for (const LVSymbol *Symbol : *getSymbols()) + if (Symbol->getHasReferenceAbstract()) + RemoveSymbol(References, Symbol->getReference()); + + // If we have elements left in 'References', those are the elements that + // need to be inserted in the current scope. + if (References.size()) { + LLVM_DEBUG({ + dbgs() << "Insert Missing Inlined Elements\n" + << "Offset = " << hexSquareString(getOffset()) << " " + << "Abstract = " << hexSquareString(Reference->getOffset()) + << "\n"; + }); + for (LVSymbol *Reference : References) { + LLVM_DEBUG({ + dbgs() << "Missing Offset = " << hexSquareString(Reference->getOffset()) + << "\n"; + }); + // We can't clone the abstract origin reference, as it contain extra + // information that is incorrect for the element to be inserted. + // As the symbol being added does not exist in the debug section, + // use its parent scope offset, to indicate its DIE location. + LVSymbol *Symbol = new LVSymbol(); + addElement(Symbol); + Symbol->setOffset(getOffset()); + Symbol->setIsOptimized(); + Symbol->setReference(Reference); + + // The symbol can be a constant, parameter or variable. + if (Reference->getIsConstant()) + Symbol->setIsConstant(); + else if (Reference->getIsParameter()) + Symbol->setIsParameter(); + else if (Reference->getIsVariable()) + Symbol->setIsVariable(); + else + llvm_unreachable("Invalid symbol kind."); + } + } +} + +void LVScope::updateLevel(LVScope *Parent, bool Moved) { + // Update the level for the element itself and all its children, using the + // given scope parent as reference. + setLevel(Parent->getLevel() + 1); + + // Update the children. + if (Children) + for (LVElement *Element : *Children) + Element->updateLevel(this, Moved); + + // Update any lines. + if (Lines) + for (LVLine *Line : *Lines) + Line->updateLevel(this, Moved); +} + +void LVScope::resolve() { + if (getIsResolved()) + return; + + // Resolve the element itself. + LVElement::resolve(); + + // Resolve the children. + if (Children) + for (LVElement *Element : *Children) { + if (getIsGlobalReference()) + // If the scope is a global reference, mark all its children as well. + Element->setIsGlobalReference(); + Element->resolve(); + } +} + +void LVScope::resolveName() { + if (getIsResolvedName()) + return; + setIsResolvedName(); + + // If the scope is a template, resolve the template parameters and get + // the name for the template with the encoded arguments. + if (getIsTemplate()) + resolveTemplate(); + else { + if (LVElement *BaseType = getType()) { + BaseType->resolveName(); + resolveFullname(BaseType); + } + } + + // In the case of unnamed scopes, try to generate a name for it, using + // the parents name and the line information. In the case of compiler + // generated functions, use its linkage name if is available. + if (!isNamed()) { + if (getIsArtificial()) + setName(getLinkageName()); + else + generateName(); + } + + LVElement::resolveName(); + + // Resolve any given pattern. + patterns().resolvePatternMatch(this); +} + +void LVScope::resolveReferences() { + // The scopes can have the following references to other elements: + // A type: + // DW_AT_type -> Type or Scope + // DW_AT_import -> Type + // A Reference: + // DW_AT_specification -> Scope + // DW_AT_abstract_origin -> Scope + // DW_AT_extension -> Scope + + // Resolve any referenced scope. + LVScope *Reference = getReference(); + if (Reference) { + Reference->resolve(); + // Recursively resolve the scope names. + resolveReferencesChain(); + } + + // Set the file/line information using the Debug Information entry. + setFile(Reference); + + // Resolve any referenced type or scope. + if (LVElement *Element = getType()) + Element->resolve(); +} + +void LVScope::resolveElements() { + // The current element represents the Root. Traverse each Compile Unit. + if (!Scopes) + return; + + for (LVScope *Scope : *Scopes) { + LVScopeCompileUnit *CompileUnit = static_cast<LVScopeCompileUnit *>(Scope); + getReader().setCompileUnit(CompileUnit); + CompileUnit->resolve(); + // Propagate any matching information into the scopes tree. + CompileUnit->propagatePatternMatch(); + } +} + +StringRef LVScope::resolveReferencesChain() { + // If the scope has a DW_AT_specification or DW_AT_abstract_origin, + // follow the chain to resolve the name from those references. + if (getHasReference() && !isNamed()) + setName(getReference()->resolveReferencesChain()); + + return getName(); +} + +// Get template parameter types. +bool LVScope::getTemplateParameterTypes(LVTypes &Params) { + // Traverse the scope types and populate the given container with those + // types that are template parameters; that container will be used by + // 'encodeTemplateArguments' to resolve them. + if (const LVTypes *Types = getTypes()) + for (LVType *Type : *Types) + if (Type->getIsTemplateParam()) { + Type->resolve(); + Params.push_back(Type); + } + + return !Params.empty(); +} + +// Resolve the template parameters/arguments relationship. +void LVScope::resolveTemplate() { + if (getIsTemplateResolved()) + return; + setIsTemplateResolved(); + + // Check if we need to encode the template arguments. + if (options().getAttributeEncoded()) { + LVTypes Params; + if (getTemplateParameterTypes(Params)) { + std::string EncodedArgs; + // Encode the arguments as part of the template name and update the + // template name, to reflect the encoded parameters. + encodeTemplateArguments(EncodedArgs, &Params); + setEncodedArgs(EncodedArgs); + } + } +} + +// Get the qualified name for the template. +void LVScope::getQualifiedName(std::string &QualifiedName) const { + if (getIsRoot() || getIsCompileUnit()) + return; + + if (LVScope *Parent = getParentScope()) + Parent->getQualifiedName(QualifiedName); + if (!QualifiedName.empty()) + QualifiedName.append("::"); + QualifiedName.append(std::string(getName())); +} + +// Encode the template arguments as part of the template name. +void LVScope::encodeTemplateArguments(std::string &Name) const { + // Qualify only when we are expanding parameters that are template + // instances; the debugger will assume the current scope symbol as + // the qualifying tag for the symbol being generated, which gives: + // namespace std { + // ... + // set<float,std::less<float>,std::allocator<float>> + // ... + // } + // The 'set' symbol is assumed to have the qualified tag 'std'. + + // We are resolving a template parameter which is another template. If + // it is already resolved, just get the qualified name and return. + std::string BaseName; + getQualifiedName(BaseName); + if (getIsTemplateResolved()) + Name.append(BaseName); +} + +void LVScope::encodeTemplateArguments(std::string &Name, + const LVTypes *Types) const { + // The encoded string will start with the scope name. + Name.append("<"); + + // The list of types are the template parameters. + if (Types) { + bool AddComma = false; + for (const LVType *Type : *Types) { + if (AddComma) + Name.append(", "); + Type->encodeTemplateArgument(Name); + AddComma = true; + } + } + + Name.append(">"); +} + +bool LVScope::resolvePrinting() const { + // The warnings collected during the scope creation as per compile unit. + // If there is a request for printing warnings, always print its associate + // Compile Unit. + if (options().getPrintWarnings() && (getIsRoot() || getIsCompileUnit())) + return true; + + // In selection mode, always print the root scope regardless of the + // number of matched elements. If no matches, the root by itself will + // indicate no matches. + if (options().getSelectExecute()) { + return getIsRoot() || getIsCompileUnit() || getHasPattern(); + } + + bool Globals = options().getAttributeGlobal(); + bool Locals = options().getAttributeLocal(); + if ((Globals && Locals) || (!Globals && !Locals)) { + // Print both Global and Local. + } else { + // Check for Global or Local Objects. + if ((Globals && !(getHasGlobals() || getIsGlobalReference())) || + (Locals && !(getHasLocals() || !getIsGlobalReference()))) + return false; + } + + // For the case of functions, skip it if is compiler generated. + if (getIsFunction() && getIsArtificial() && + !options().getAttributeGenerated()) + return false; + + return true; +} + +Error LVScope::doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, + bool Full) const { + // During a view output splitting, use the output stream created by the + // split context, then switch to the reader output stream. + raw_ostream *StreamSplit = &OS; + + // If 'Split', we use the scope name (CU name) as the ouput file; the + // delimiters in the pathname, must be replaced by a normal character. + if (getIsCompileUnit()) { + getReader().setCompileUnit(const_cast<LVScope *>(this)); + if (Split) { + std::string ScopeName(getName()); + if (std::error_code EC = + getReaderSplitContext().open(ScopeName, ".txt", OS)) + return createStringError(EC, "Unable to create split output file %s", + ScopeName.c_str()); + StreamSplit = static_cast<raw_ostream *>(&getReaderSplitContext().os()); + } + } + + // Ignore discarded or stripped scopes (functions). + bool DoPrint = (options().getAttributeDiscarded()) ? true : !getIsDiscarded(); + + // If we are in compare mode, the only conditions are related to the + // element being missing. In the case of elements comparison, we print the + // augmented view, that includes added elements. + // In print mode, we check other conditions, such as local, global, etc. + if (DoPrint) { + DoPrint = + getIsInCompare() ? options().getReportExecute() : resolvePrinting(); + } + + // At this point we have checked for very specific options, to decide if the + // element will be printed. Include the caller's test for element general + // print. + DoPrint = DoPrint && (Print || options().getOutputSplit()); + + if (DoPrint) { + // Print the element itself. + print(*StreamSplit, Full); + + // Check if we have reached the requested lexical level specified in the + // command line options. Input file is level zero and the CU is level 1. + if ((getIsRoot() || options().getPrintAnyElement()) && + options().getPrintFormatting() && + getLevel() < options().getOutputLevel()) { + // Print the children. + if (Children) + for (const LVElement *Element : *Children) { + if (Match && !Element->getHasPattern()) + continue; + if (Error Err = + Element->doPrint(Split, Match, Print, *StreamSplit, Full)) + return Err; + } + + // Print the line records. + if (Lines) + for (const LVLine *Line : *Lines) { + if (Match && !Line->getHasPattern()) + continue; + if (Error Err = + Line->doPrint(Split, Match, Print, *StreamSplit, Full)) + return Err; + } + + // Print the warnings. + if (options().getPrintWarnings()) + printWarnings(*StreamSplit, Full); + } + } + + // Done printing the compile unit. Print any requested summary and + // restore the original output context. + if (getIsCompileUnit()) { + if (options().getPrintSummary()) + printSummary(*StreamSplit); + if (options().getPrintSizes()) + printSizes(*StreamSplit); + if (Split) { + getReaderSplitContext().close(); + StreamSplit = &getReader().outputStream(); + } + } + + if (getIsRoot() && options().getPrintWarnings()) { + getReader().printRecords(*StreamSplit); + } + + return Error::success(); +} + +void LVScope::sort() { + // Preserve the lines order as they are associated with user code. + LVSortFunction SortFunction = getSortFunction(); + if (SortFunction) { + std::function<void(LVScope * Parent, LVSortFunction SortFunction)> Sort = + [&](LVScope *Parent, LVSortFunction SortFunction) { + auto Traverse = [&](auto *Set, LVSortFunction SortFunction) { + if (Set) + std::stable_sort(Set->begin(), Set->end(), SortFunction); + }; + Traverse(Parent->Types, SortFunction); + Traverse(Parent->Symbols, SortFunction); + Traverse(Parent->Scopes, SortFunction); + Traverse(Parent->Ranges, compareRange); + Traverse(Parent->Children, SortFunction); + + if (Parent->Scopes) + for (LVScope *Scope : *Parent->Scopes) + Sort(Scope, SortFunction); + }; + + // Start traversing the scopes root and transform the element name. + Sort(this, SortFunction); + } +} + +void LVScope::traverseParents(LVScopeGetFunction GetFunction, + LVScopeSetFunction SetFunction) { + // Traverse the parent tree. + LVScope *Parent = this; + while (Parent) { + // Terminates if the 'SetFunction' has been already executed. + if ((Parent->*GetFunction)()) + break; + (Parent->*SetFunction)(); + Parent = Parent->getParentScope(); + } +} + +void LVScope::traverseParentsAndChildren(LVObjectGetFunction GetFunction, + LVObjectSetFunction SetFunction) { + if (options().getReportParents()) { + // First traverse the parent tree. + LVScope *Parent = this; + while (Parent) { + // Terminates if the 'SetFunction' has been already executed. + if ((Parent->*GetFunction)()) + break; + (Parent->*SetFunction)(); + Parent = Parent->getParentScope(); + } + } + + std::function<void(LVScope * Scope)> TraverseChildren = [&](LVScope *Scope) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + (Entry->*SetFunction)(); + }; + + (Scope->*SetFunction)(); + + Traverse(Scope->getTypes()); + Traverse(Scope->getSymbols()); + Traverse(Scope->getLines()); + + if (const LVScopes *Scopes = Scope->getScopes()) + for (LVScope *Scope : *Scopes) + TraverseChildren(Scope); + }; + + if (options().getReportChildren()) + TraverseChildren(this); +} + +// Traverse the symbol location ranges and for each range: +// - Apply the 'ValidLocation' validation criteria. +// - Add any failed range to the 'LocationList'. +// - Calculate location coverage. +void LVScope::getLocations(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + // Traverse scopes and symbols. + if (Symbols) + for (LVSymbol *Symbol : *Symbols) + Symbol->getLocations(LocationList, ValidLocation, RecordInvalid); + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getLocations(LocationList, ValidLocation, RecordInvalid); +} + +// Traverse the scope ranges and for each range: +// - Apply the 'ValidLocation' validation criteria. +// - Add any failed range to the 'LocationList'. +// - Calculate location coverage. +void LVScope::getRanges(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + // Ignore discarded or stripped scopes (functions). + if (getIsDiscarded()) + return; + + // Process the ranges for current scope. + if (Ranges) { + for (LVLocation *Location : *Ranges) { + // Add the invalid location object. + if (!(Location->*ValidLocation)() && RecordInvalid) + LocationList.push_back(Location); + } + + // Calculate coverage factor. + calculateCoverage(); + } + + // Traverse the scopes. + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getRanges(LocationList, ValidLocation, RecordInvalid); +} + +// Get all the ranges associated with scopes. +void LVScope::getRanges(LVRange &RangeList) { + // Ignore discarded or stripped scopes (functions). + if (getIsDiscarded()) + return; + + if (Ranges) + RangeList.addEntry(this); + if (Scopes) + for (LVScope *Scope : *Scopes) + Scope->getRanges(RangeList); +} + +LVScope *LVScope::outermostParent(LVAddress Address) { + LVScope *Parent = this; + while (Parent) { + const LVLocations *ParentRanges = Parent->getRanges(); + if (ParentRanges) + for (const LVLocation *Location : *ParentRanges) + if (Location->getLowerAddress() <= Address) + return Parent; + Parent = Parent->getParentScope(); + } + 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) { + for (const LVLocation *Location : *Ranges) + Location->print(OS, Full); + } +} + +void LVScope::printEncodedArgs(raw_ostream &OS, bool Full) const { + if (options().getPrintFormatting() && options().getAttributeEncoded()) + printAttributes(OS, Full, "{Encoded} ", const_cast<LVScope *>(this), + getEncodedArgs(), /*UseQuotes=*/false, /*PrintRef=*/false); +} + +void LVScope::print(raw_ostream &OS, bool Full) const { + if (getIncludeInPrint() && getReader().doPrintScope(this)) { + // For a summary (printed elements), do not count the scope root. + // For a summary (selected elements) do not count a compile unit. + if (!(getIsRoot() || (getIsCompileUnit() && options().getSelectExecute()))) + getReaderCompileUnit()->incrementPrintedScopes(); + LVElement::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVScope::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()); + // Do not print any type or name for a lexical block. + if (!getIsBlock()) { + OS << " " << formattedName(getName()); + if (!getIsAggregate()) + OS << " -> " << typeOffsetAsString() + << formattedNames(getTypeQualifiedName(), typeAsString()); + } + OS << "\n"; + + // Print any active ranges. + if (Full && getIsBlock()) + printActiveRanges(OS, Full); +} + +//===----------------------------------------------------------------------===// +// 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) { + if (getIsTemplateResolved()) + printEncodedArgs(OS, Full); + LVScope *Reference = getReference(); + if (Reference) + Reference->printReference(OS, Full, const_cast<LVScopeAggregate *>(this)); + } +} + +//===----------------------------------------------------------------------===// +// 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() + << formattedNames(getTypeQualifiedName(), typeAsString()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF array (DW_TAG_array_type). +//===----------------------------------------------------------------------===// +void LVScopeArray::resolveExtra() { + // If the scope is an array, resolve the subrange entries and get those + // values encoded and assigned to the scope type. + // Encode the array subrange entries as part of the name. + if (getIsArrayResolved()) + return; + setIsArrayResolved(); + + // There are 2 cases to represent the bounds information for an array: + // 1) DW_TAG_array_type + // DW_AT_type --> ref_type + // DW_TAG_subrange_type + // DW_AT_type --> ref_type (type of object) + // DW_AT_count --> value (number of elements in subrange) + + // 2) DW_TAG_array_type + // DW_AT_type --> ref_type + // DW_TAG_subrange_type + // DW_AT_lower_bound --> value + // DW_AT_upper_bound --> value + + // The idea is to represent the bounds as a string, depending on the format: + // 1) [count] + // 2) [lower][upper] + + // Traverse scope types, looking for those types that are subranges. + LVTypes Subranges; + if (const LVTypes *Types = getTypes()) + for (LVType *Type : *Types) + if (Type->getIsSubrange()) { + Type->resolve(); + Subranges.push_back(Type); + } + + // Use the subrange types to generate the high level name for the array. + // Check the type has been fully resolved. + if (LVElement *BaseType = getType()) { + BaseType->resolveName(); + resolveFullname(BaseType); + } + + // In 'resolveFullname' a check is done for double spaces in the type name. + std::stringstream ArrayInfo; + if (ElementType) + ArrayInfo << getTypeName().str() << " "; + + for (const LVType *Type : Subranges) { + if (Type->getIsSubrangeCount()) + // Check if we have DW_AT_count subrange style. + ArrayInfo << "[" << Type->getCount() << "]"; + else { + // Get lower and upper subrange values. + unsigned LowerBound; + unsigned UpperBound; + std::tie(LowerBound, UpperBound) = Type->getBounds(); + + // The representation depends on the bound values. If the lower value + // is zero, treat the pair as the elements count. Otherwise, just use + // the pair, as they are representing arrays in languages other than + // C/C++ and the lower limit is not zero. + if (LowerBound) + ArrayInfo << "[" << LowerBound << ".." << UpperBound << "]"; + else + ArrayInfo << "[" << UpperBound + 1 << "]"; + } + } + + // Update the scope name, to reflect the encoded subranges. + 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"; +} + +//===----------------------------------------------------------------------===// +// An object file (single or multiple CUs). +//===----------------------------------------------------------------------===// +void LVScopeCompileUnit::addSize(LVScope *Scope, LVOffset Lower, + LVOffset Upper) { + LLVM_DEBUG({ + dbgs() << format( + "CU [0x%08" PRIx64 "], Scope [0x%08" PRIx64 "], Range [0x%08" PRIx64 + ":0x%08" PRIx64 "], Size = %" PRId64 "\n", + getOffset(), Scope->getOffset(), Lower, Upper, Upper - Lower); + }); + + // There is no need to check for a previous entry, as we are traversing the + // debug information in sequential order. + LVOffset Size = Upper - Lower; + Sizes[Scope] = Size; + if (this == Scope) + // Record contribution size for the compilation unit. + CUContributionSize = Size; +} + +// Update parents and children with pattern information. +void LVScopeCompileUnit::propagatePatternMatch() { + // At this stage, we have finished creating the Scopes tree and we have + // a list of elements that match the pattern specified in the command line. + // The pattern corresponds to a scope or element; mark parents and/or + // children as having that pattern, before any printing is done. + if (!options().getSelectExecute()) + return; + + if (MatchedScopes.size()) { + for (LVScope *Scope : MatchedScopes) + Scope->traverseParentsAndChildren(&LVScope::getHasPattern, + &LVScope::setHasPattern); + } else { + // Mark the compile unit as having a pattern to enable any requests to + // print sizes and summary as that information is recorded at that level. + setHasPattern(); + } +} + +void LVScopeCompileUnit::processRangeLocationCoverage( + LVValidLocation ValidLocation) { + if (options().getAttributeRange()) { + // Traverse the scopes to get scopes that have invalid ranges. + LVLocations Locations; + bool RecordInvalid = options().getWarningRanges(); + getRanges(Locations, ValidLocation, RecordInvalid); + + // Validate ranges associated with scopes. + if (RecordInvalid) + for (LVLocation *Location : Locations) + addInvalidRange(Location); + } + + if (options().getAttributeLocation()) { + // Traverse the scopes to get locations that have invalid ranges. + LVLocations Locations; + bool RecordInvalid = options().getWarningLocations(); + getLocations(Locations, ValidLocation, RecordInvalid); + + // Validate ranges associated with locations. + if (RecordInvalid) + for (LVLocation *Location : Locations) + addInvalidLocation(Location); + } +} + +void LVScopeCompileUnit::addMapping(LVLine *Line, LVSectionIndex SectionIndex) { + LVAddress Address = Line->getOffset(); + SectionMappings.add(SectionIndex, Address, Line); +} + +LVLine *LVScopeCompileUnit::lineLowerBound(LVAddress Address, + LVScope *Scope) const { + LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope); + LVAddressToLine *Map = SectionMappings.findMap(SectionIndex); + if (!Map || Map->empty()) + return nullptr; + LVAddressToLine::const_iterator Iter = Map->lower_bound(Address); + return (Iter != Map->end()) ? Iter->second : nullptr; +} + +LVLine *LVScopeCompileUnit::lineUpperBound(LVAddress Address, + LVScope *Scope) const { + LVSectionIndex SectionIndex = getReader().getSectionIndex(Scope); + LVAddressToLine *Map = SectionMappings.findMap(SectionIndex); + if (!Map || Map->empty()) + return nullptr; + LVAddressToLine::const_iterator Iter = Map->upper_bound(Address); + if (Iter != Map->begin()) + Iter = std::prev(Iter); + return Iter->second; +} + +LVLineRange LVScopeCompileUnit::lineRange(LVLocation *Location) const { + // The parent of a location can be a symbol or a scope. + LVElement *Element = Location->getParent(); + LVScope *Parent = Element->getIsScope() ? static_cast<LVScope *>(Element) + : Element->getParentScope(); + LVLine *LowLine = lineLowerBound(Location->getLowerAddress(), Parent); + LVLine *HighLine = lineUpperBound(Location->getUpperAddress(), Parent); + return LVLineRange(LowLine, HighLine); +} + +StringRef LVScopeCompileUnit::getFilename(size_t Index) const { + if (Index <= 0 || Index > Filenames.size()) + return StringRef(); + 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; +} +void LVScopeCompileUnit::incrementPrintedScopes() { + options().getSelectExecute() ? ++Found.Scopes : ++Printed.Scopes; +} +void LVScopeCompileUnit::incrementPrintedSymbols() { + options().getSelectExecute() ? ++Found.Symbols : ++Printed.Symbols; +} +void LVScopeCompileUnit::incrementPrintedTypes() { + options().getSelectExecute() ? ++Found.Types : ++Printed.Types; +} + +// Values are used by '--summary' option (allocated). +void LVScopeCompileUnit::increment(LVLine *Line) { + if (Line->getIncludeInPrint()) + ++Allocated.Lines; +} +void LVScopeCompileUnit::increment(LVScope *Scope) { + if (Scope->getIncludeInPrint()) + ++Allocated.Scopes; +} +void LVScopeCompileUnit::increment(LVSymbol *Symbol) { + if (Symbol->getIncludeInPrint()) + ++Allocated.Symbols; +} +void LVScopeCompileUnit::increment(LVType *Type) { + if (Type->getIncludeInPrint()) + ++Allocated.Types; +} + +// A new element has been added to the scopes tree. Take the following steps: +// Increase the added element counters, for printing summary. +// 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) { + addItem<LVTagOffsetsMap, LVOffsetList, dwarf::Tag, LVOffset>(&DebugTags, + Target, Offset); +} + +// Record elements with invalid offsets. +void LVScopeCompileUnit::addInvalidOffset(LVOffset Offset, LVElement *Element) { + if (WarningOffsets.find(Offset) == WarningOffsets.end()) + WarningOffsets.emplace(Offset, Element); +} + +// Record symbols with invalid coverage values. +void LVScopeCompileUnit::addInvalidCoverage(LVSymbol *Symbol) { + LVOffset Offset = Symbol->getOffset(); + if (InvalidCoverages.find(Offset) == InvalidCoverages.end()) + InvalidCoverages.emplace(Offset, Symbol); +} + +// Record symbols with invalid locations. +void LVScopeCompileUnit::addInvalidLocation(LVLocation *Location) { + addInvalidLocationOrRange(Location, Location->getParentSymbol(), + &InvalidLocations); +} + +// Record scopes with invalid ranges. +void LVScopeCompileUnit::addInvalidRange(LVLocation *Location) { + addInvalidLocationOrRange(Location, Location->getParentScope(), + &InvalidRanges); +} + +// Record line zero. +void LVScopeCompileUnit::addLineZero(LVLine *Line) { + LVScope *Scope = Line->getParentScope(); + LVOffset Offset = Scope->getOffset(); + addInvalidOffset(Offset, Scope); + addItem<LVOffsetLinesMap, LVLines, LVOffset, LVLine *>(&LinesZero, Offset, + Line); +} + +void LVScopeCompileUnit::printLocalNames(raw_ostream &OS, bool Full) const { + if (!options().getPrintFormatting()) + return; + + // Calculate an indentation value, to preserve a nice layout. + size_t Indentation = options().indentationSize() + + lineNumberAsString().length() + + indentAsString(getLevel() + 1).length() + 3; + + enum class Option { Directory, File }; + auto PrintNames = [&](Option Action) { + StringRef Kind = Action == Option::Directory ? "Directory" : "File"; + std::set<std::string> UniqueNames; + for (size_t Index : Filenames) { + // In the case of missing directory name in the .debug_line table, + // the returned string has a leading '/'. + StringRef Name = getStringPool().getString(Index); + size_t Pos = Name.rfind('/'); + if (Pos != std::string::npos) + Name = (Action == Option::File) ? Name.substr(Pos + 1) + : Name.substr(0, Pos); + // Collect only unique names. + UniqueNames.insert(std::string(Name)); + } + for (const std::string &Name : UniqueNames) + OS << std::string(Indentation, ' ') << formattedKind(Kind) << " " + << formattedName(Name) << "\n"; + }; + + if (options().getAttributeDirectories()) + PrintNames(Option::Directory); + if (options().getAttributeFiles()) + PrintNames(Option::File); + if (options().getAttributePublics()) { + StringRef Kind = "Public"; + // The public names are indexed by 'LVScope *'. We want to print + // them by logical element address, to show the scopes layout. + using OffsetSorted = std::map<LVAddress, LVPublicNames::const_iterator>; + OffsetSorted SortedNames; + for (LVPublicNames::const_iterator Iter = PublicNames.begin(); + Iter != PublicNames.end(); ++Iter) + SortedNames.emplace(Iter->first->getOffset(), Iter); + + LVPublicNames::const_iterator Iter; + for (OffsetSorted::reference Entry : SortedNames) { + Iter = Entry.second; + OS << std::string(Indentation, ' ') << formattedKind(Kind) << " " + << formattedName((*Iter).first->getName()); + if (options().getAttributeOffset()) { + LVAddress Address = (*Iter).second.first; + size_t Size = (*Iter).second.second; + OS << " [" << hexString(Address) << ":" << hexString(Address + Size) + << "]"; + } + OS << "\n"; + } + } +} + +void LVScopeCompileUnit::printWarnings(raw_ostream &OS, bool Full) const { + auto PrintHeader = [&](const char *Header) { OS << "\n" << Header << ":\n"; }; + auto PrintFooter = [&](auto &Set) { + if (Set.empty()) + OS << "None\n"; + }; + auto PrintOffset = [&](unsigned &Count, LVOffset Offset) { + if (Count == 5) { + Count = 0; + OS << "\n"; + } + ++Count; + OS << hexSquareString(Offset) << " "; + }; + auto PrintElement = [&](const LVOffsetElementMap &Map, LVOffset Offset) { + LVOffsetElementMap::const_iterator Iter = Map.find(Offset); + LVElement *Element = Iter != Map.end() ? Iter->second : nullptr; + OS << "[" << hexString(Offset) << "]"; + if (Element) + OS << " " << formattedKind(Element->kind()) << " " + << formattedName(Element->getName()); + OS << "\n"; + }; + auto PrintInvalidLocations = [&](const LVOffsetLocationsMap &Map, + const char *Header) { + PrintHeader(Header); + for (LVOffsetLocationsMap::const_reference Entry : Map) { + PrintElement(WarningOffsets, Entry.first); + for (const LVLocation *Location : *Entry.second) + OS << hexSquareString(Location->getOffset()) << " " + << Location->getIntervalInfo() << "\n"; + } + PrintFooter(Map); + }; + + if (options().getInternalTag() && getReader().isBinaryTypeELF()) { + PrintHeader("Unsupported DWARF Tags"); + for (LVTagOffsetsMap::const_reference Entry : DebugTags) { + OS << format("\n0x%02x", (unsigned)Entry.first) << ", " + << dwarf::TagString(Entry.first) << "\n"; + unsigned Count = 0; + for (const LVOffset &Offset : *Entry.second) + PrintOffset(Count, Offset); + OS << "\n"; + } + PrintFooter(DebugTags); + } + + if (options().getWarningCoverages()) { + PrintHeader("Symbols Invalid Coverages"); + for (LVOffsetSymbolMap::const_reference Entry : InvalidCoverages) { + // Symbol basic information. + LVSymbol *Symbol = Entry.second; + OS << hexSquareString(Entry.first) << " {Coverage} " + << format("%.2f%%", Symbol->getCoveragePercentage()) << " " + << formattedKind(Symbol->kind()) << " " + << formattedName(Symbol->getName()) << "\n"; + } + PrintFooter(InvalidCoverages); + } + + if (options().getWarningLines()) { + PrintHeader("Lines Zero References"); + for (LVOffsetLinesMap::const_reference Entry : LinesZero) { + PrintElement(WarningOffsets, Entry.first); + unsigned Count = 0; + for (const LVLine *Line : *Entry.second) + PrintOffset(Count, Line->getOffset()); + OS << "\n"; + } + PrintFooter(LinesZero); + } + + if (options().getWarningLocations()) + PrintInvalidLocations(InvalidLocations, "Invalid Location Ranges"); + + if (options().getWarningRanges()) + PrintInvalidLocations(InvalidRanges, "Invalid Code Ranges"); +} + +void LVScopeCompileUnit::printTotals(raw_ostream &OS) const { + OS << "\nTotals by lexical level:\n"; + for (size_t Index = 1; Index <= MaxSeenLevel; ++Index) + OS << format("[%03d]: %10d (%6.2f%%)\n", Index, Totals[Index].first, + Totals[Index].second); +} + +void LVScopeCompileUnit::printScopeSize(const LVScope *Scope, raw_ostream &OS) { + LVSizesMap::const_iterator Iter = Sizes.find(Scope); + if (Iter != Sizes.end()) { + LVOffset Size = Iter->second; + assert(CUContributionSize && "Invalid CU contribution size."); + // Get a percentage rounded to two decimal digits. This avoids + // implementation-defined rounding inside printing functions. + float Percentage = + rint((float(Size) / CUContributionSize) * 100.0 * 100.0) / 100.0; + OS << format("%10" PRId64 " (%6.2f%%) : ", Size, Percentage); + Scope->print(OS); + + // Keep record of the total sizes at each lexical level. + LVLevel Level = Scope->getLevel(); + if (Level > MaxSeenLevel) + MaxSeenLevel = Level; + if (Level >= Totals.size()) + Totals.resize(2 * Level); + Totals[Level].first += Size; + Totals[Level].second += Percentage; + } +} + +void LVScopeCompileUnit::printSizes(raw_ostream &OS) const { + // Recursively print the contributions for each scope. + std::function<void(const LVScope *Scope)> PrintScope = + [&](const LVScope *Scope) { + // If we have selection criteria, then use only the selected scopes. + if (options().getSelectExecute() && options().getReportAnyView()) { + for (const LVScope *Scope : MatchedScopes) + if (Scope->getLevel() < options().getOutputLevel()) + printScopeSize(Scope, OS); + return; + } + if (Scope->getLevel() < options().getOutputLevel()) { + if (const LVScopes *Scopes = Scope->getScopes()) + for (const LVScope *Scope : *Scopes) { + printScopeSize(Scope, OS); + PrintScope(Scope); + } + } + }; + + bool PrintScopes = options().getPrintScopes(); + if (!PrintScopes) + options().setPrintScopes(); + getReader().setCompileUnit(const_cast<LVScopeCompileUnit *>(this)); + + OS << "\nScope Sizes:\n"; + options().resetPrintFormatting(); + options().setPrintOffset(); + + // Print the scopes regardless if the user has requested any scopes + // printing. Set the option just to allow printing the contributions. + printScopeSize(this, OS); + PrintScope(this); + + // Print total scope sizes by level. + printTotals(OS); + + options().resetPrintOffset(); + options().setPrintFormatting(); + + if (!PrintScopes) + options().resetPrintScopes(); +} + +void LVScopeCompileUnit::printSummary(raw_ostream &OS) const { + printSummary(OS, options().getSelectExecute() ? Found : Printed, "Printed"); +} + +// Print summary details for the scopes tree. +void LVScopeCompileUnit::printSummary(raw_ostream &OS, const LVCounter &Counter, + const char *Header) const { + std::string Separator = std::string(29, '-'); + auto PrintSeparator = [&]() { OS << Separator << "\n"; }; + auto PrintHeadingRow = [&](const char *T, const char *U, const char *V) { + OS << format("%-9s%9s %9s\n", T, U, V); + }; + auto PrintDataRow = [&](const char *T, unsigned U, unsigned V) { + OS << format("%-9s%9d %9d\n", T, U, V); + }; + + OS << "\n"; + PrintSeparator(); + PrintHeadingRow("Element", "Total", Header); + PrintSeparator(); + PrintDataRow("Scopes", Allocated.Scopes, Counter.Scopes); + PrintDataRow("Symbols", Allocated.Symbols, Counter.Symbols); + PrintDataRow("Types", Allocated.Types, Counter.Types); + PrintDataRow("Lines", Allocated.Lines, Counter.Lines); + PrintSeparator(); + PrintDataRow( + "Total", + Allocated.Scopes + Allocated.Symbols + Allocated.Lines + Allocated.Types, + Counter.Scopes + Counter.Symbols + Counter.Lines + Counter.Types); +} + +void LVScopeCompileUnit::printMatchedElements(raw_ostream &OS, + bool UseMatchedElements) { + LVSortFunction SortFunction = getSortFunction(); + if (SortFunction) + std::stable_sort(MatchedElements.begin(), MatchedElements.end(), + SortFunction); + + // Check the type of elements required to be printed. 'MatchedElements' + // contains generic elements (lines, scopes, symbols, types). If we have a + // request to print any generic element, then allow the normal printing. + if (options().getPrintAnyElement()) { + if (UseMatchedElements) + OS << "\n"; + print(OS); + + if (UseMatchedElements) { + // Print the details for the matched elements. + for (const LVElement *Element : MatchedElements) + Element->print(OS); + } else { + // Print the view for the matched scopes. + for (const LVScope *Scope : MatchedScopes) { + Scope->print(OS); + if (const LVElements *Elements = Scope->getChildren()) + for (LVElement *Element : *Elements) + Element->print(OS); + } + } + + // Print any requested summary. + if (options().getPrintSummary()) { + // In the case of '--report=details' the matched elements are + // already counted; just proceed to print any requested summary. + // Otherwise, count them and print the summary. + if (!options().getReportList()) { + for (LVElement *Element : MatchedElements) { + if (!Element->getIncludeInPrint()) + continue; + if (Element->getIsType()) + ++Found.Types; + else if (Element->getIsSymbol()) + ++Found.Symbols; + else if (Element->getIsScope()) + ++Found.Scopes; + else if (Element->getIsLine()) + ++Found.Lines; + else + assert(Element && "Invalid element."); + } + } + printSummary(OS, Found, "Printed"); + } + } + + // Check if we have a request to print sizes for the matched elements + // that are scopes. + if (options().getPrintSizes()) { + OS << "\n"; + print(OS); + + OS << "\nScope Sizes:\n"; + printScopeSize(this, OS); + for (LVElement *Element : MatchedElements) + if (Element->getIsScope()) + // Print sizes only for scopes. + printScopeSize(static_cast<LVScope *>(Element), OS); + + printTotals(OS); + } +} + +void LVScopeCompileUnit::print(raw_ostream &OS, bool Full) const { + // Reset counters for printed and found elements. + const_cast<LVScopeCompileUnit *>(this)->Found.reset(); + const_cast<LVScopeCompileUnit *>(this)->Printed.reset(); + + if (getReader().doPrintScope(this) && options().getPrintFormatting()) + OS << "\n"; + + LVScope::print(OS, Full); +} + +void LVScopeCompileUnit::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " '" << getName() << "'\n"; + if (options().getPrintFormatting() && options().getAttributeProducer()) + printAttributes(OS, Full, "{Producer} ", + const_cast<LVScopeCompileUnit *>(this), getProducer(), + /*UseQuotes=*/true, + /*PrintRef=*/false); + + // Reset file index, to allow its children to print the correct filename. + options().resetFilenameIndex(); + + // Print any files, directories, public names and active ranges. + if (Full) { + printLocalNames(OS, Full); + printActiveRanges(OS, Full); + } +} + +//===----------------------------------------------------------------------===// +// 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 " : "") + << formattedName(getName()); + if (getHasType()) + OS << " -> " << typeOffsetAsString() + << formattedNames(getTypeQualifiedName(), typeAsString()); + OS << "\n"; +} + +//===----------------------------------------------------------------------===// +// 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"; +} + +//===----------------------------------------------------------------------===// +// DWARF function. +//===----------------------------------------------------------------------===// +void LVScopeFunction::resolveReferences() { + // Before we resolve any references to other elements, check if we have + // to insert missing elements, that have been stripped, which will help + // the logical view comparison. + if (options().getAttributeInserted() && getHasReferenceAbstract() && + !getAddedMissing()) { + // Add missing elements at the function scope. + addMissingElements(getReference()); + if (Scopes) + for (LVScope *Scope : *Scopes) + if (Scope->getHasReferenceAbstract() && !Scope->getAddedMissing()) + Scope->addMissingElements(Scope->getReference()); + } + + LVScope::resolveReferences(); + + // The DWARF 'extern' attribute is generated at the class level. + // 0000003f DW_TAG_class_type "CLASS" + // 00000048 DW_TAG_subprogram "bar" + // DW_AT_external DW_FORM_flag_present + // 00000070 DW_TAG_subprogram "bar" + // DW_AT_specification DW_FORM_ref4 0x00000048 + // If there is a reference linking the declaration and definition, mark + // the definition as extern, to facilitate the logical view comparison. + if (getHasReferenceSpecification()) { + LVScope *Reference = getReference(); + if (Reference && Reference->getIsExternal()) { + Reference->resetIsExternal(); + setIsExternal(); + } + } + + // Resolve the function associated type. + if (!getType()) + if (LVScope *Reference = getReference()) + setType(Reference->getType()); +} + +void LVScopeFunction::setName(StringRef ObjectName) { + LVScope::setName(ObjectName); + // Check for system generated functions. + getReader().isSystemEntry(this, ObjectName); +} + +void LVScopeFunction::resolveExtra() { + // Check if we need to encode the template arguments. + if (getIsTemplate()) + 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(); + + // Inline attributes based on the reference element. + uint32_t InlineCode = + Reference ? Reference->getInlineCode() : getInlineCode(); + + // Accessibility depends on the parent (class, structure). + uint32_t AccessCode = 0; + if (getIsMember()) + AccessCode = getParentScope()->getIsClass() ? dwarf::DW_ACCESS_private + : dwarf::DW_ACCESS_public; + + std::string Attributes = + getIsCallSite() + ? "" + : formatAttributes(externalString(), accessibilityString(AccessCode), + inlineCodeString(InlineCode), virtualityString()); + + OS << formattedKind(kind()) << " " << Attributes << formattedName(getName()) + << discriminatorAsString() << " -> " << typeOffsetAsString() + << formattedNames(getTypeQualifiedName(), typeAsString()) << "\n"; + + // Print any active ranges. + if (Full) { + if (getIsTemplateResolved()) + printEncodedArgs(OS, Full); + printActiveRanges(OS, Full); + if (getLinkageNameIndex()) + printLinkageName(OS, Full, const_cast<LVScopeFunction *>(this), + const_cast<LVScopeFunction *>(this)); + if (Reference) + Reference->printReference(OS, Full, const_cast<LVScopeFunction *>(this)); + } +} + +//===----------------------------------------------------------------------===// +// DWARF inlined function (DW_TAG_inlined_function). +//===----------------------------------------------------------------------===// +void LVScopeFunctionInlined::resolveExtra() { + // Check if we need to encode the template arguments. + if (getIsTemplate()) + 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); +} + +//===----------------------------------------------------------------------===// +// DWARF subroutine type. +//===----------------------------------------------------------------------===// +// Resolve a Subroutine Type (Callback). +void LVScopeFunctionType::resolveExtra() { + if (getIsMemberPointerResolved()) + return; + setIsMemberPointerResolved(); + + // The encoded string has the return type and the formal parameters type. + std::string Name(typeAsString()); + Name.append(" (*)"); + Name.append("("); + + // Traverse the scope symbols, looking for those which are parameters. + if (const LVSymbols *Symbols = getSymbols()) { + bool AddComma = false; + for (LVSymbol *Symbol : *Symbols) + if (Symbol->getIsParameter()) { + Symbol->resolve(); + if (LVElement *Type = Symbol->getType()) + Type->resolveName(); + if (AddComma) + Name.append(", "); + Name.append(std::string(Symbol->getTypeName())); + AddComma = true; + } + } + + Name.append(")"); + + // Update the scope name, to reflect the encoded parameters. + setName(Name); +} + +//===----------------------------------------------------------------------===// +// 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"; + + // Print any active ranges. + if (Full) { + printActiveRanges(OS, Full); + + if (LVScope *Reference = getReference()) + Reference->printReference(OS, Full, const_cast<LVScopeNamespace *>(this)); + } +} + +//===----------------------------------------------------------------------===// +// An object file (single or multiple CUs). +//===----------------------------------------------------------------------===// +void LVScopeRoot::processRangeInformation() { + if (!options().getAttributeAnyLocation()) + return; + + if (Scopes) + for (LVScope *Scope : *Scopes) { + LVScopeCompileUnit *CompileUnit = + static_cast<LVScopeCompileUnit *>(Scope); + getReader().setCompileUnit(CompileUnit); + CompileUnit->processRangeLocationCoverage(); + } +} + +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); +} + +void LVScopeRoot::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << ""; + if (options().getAttributeFormat()) + OS << " -> " << getFileFormatName(); + OS << "\n"; +} + +Error LVScopeRoot::doPrintMatches(bool Split, raw_ostream &OS, + bool UseMatchedElements) const { + // During a view output splitting, use the output stream created by the + // split context, then switch to the reader output stream. + static raw_ostream *StreamSplit = &OS; + + if (Scopes) { + if (UseMatchedElements) + options().resetPrintFormatting(); + print(OS); + + for (LVScope *Scope : *Scopes) { + getReader().setCompileUnit(const_cast<LVScope *>(Scope)); + + // If 'Split', we use the scope name (CU name) as the ouput file; the + // delimiters in the pathname, must be replaced by a normal character. + if (Split) { + std::string ScopeName(Scope->getName()); + if (std::error_code EC = + getReaderSplitContext().open(ScopeName, ".txt", OS)) + return createStringError(EC, "Unable to create split output file %s", + ScopeName.c_str()); + StreamSplit = static_cast<raw_ostream *>(&getReaderSplitContext().os()); + } + + Scope->printMatchedElements(*StreamSplit, UseMatchedElements); + + // Done printing the compile unit. Restore the original output context. + if (Split) { + getReaderSplitContext().close(); + StreamSplit = &getReader().outputStream(); + } + } + if (UseMatchedElements) + options().setPrintFormatting(); + } + + return Error::success(); +} + +//===----------------------------------------------------------------------===// +// 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/LVSort.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp new file mode 100644 index 000000000000..ea01671d0eb1 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp @@ -0,0 +1,113 @@ +//===-- LVSort.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 +// +//===----------------------------------------------------------------------===// +// +// Support for LVObject sorting. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVSort.h" +#include "llvm/DebugInfo/LogicalView/Core/LVElement.h" +#include "llvm/DebugInfo/LogicalView/Core/LVReader.h" +#include <string> + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Sort" + +//===----------------------------------------------------------------------===// +// Callback functions to sort objects. +//===----------------------------------------------------------------------===// +// Callback comparator based on kind. +LVSortValue llvm::logicalview::compareKind(const LVObject *LHS, + const LVObject *RHS) { + return std::string(LHS->kind()) < std::string(RHS->kind()); +} + +// Callback comparator based on line. +LVSortValue llvm::logicalview::compareLine(const LVObject *LHS, + const LVObject *RHS) { + return LHS->getLineNumber() < RHS->getLineNumber(); +} + +// Callback comparator based on name. +LVSortValue llvm::logicalview::compareName(const LVObject *LHS, + const LVObject *RHS) { + return LHS->getName() < RHS->getName(); +} + +// Callback comparator based on DIE offset. +LVSortValue llvm::logicalview::compareOffset(const LVObject *LHS, + const LVObject *RHS) { + return LHS->getOffset() < RHS->getOffset(); +} + +// Callback comparator for Range compare. +LVSortValue llvm::logicalview::compareRange(const LVObject *LHS, + const LVObject *RHS) { + if (LHS->getLowerAddress() < RHS->getLowerAddress()) + return true; + + // If the lower address is the same, use the upper address value in + // order to put first the smallest interval. + if (LHS->getLowerAddress() == RHS->getLowerAddress()) + return LHS->getUpperAddress() < RHS->getUpperAddress(); + + return false; +} + +// Callback comparator based on multiple keys (First: Kind). +LVSortValue llvm::logicalview::sortByKind(const LVObject *LHS, + const LVObject *RHS) { + // Order in which the object attributes are used for comparison: + // kind, name, line number, offset. + std::tuple<std::string, StringRef, uint32_t, LVOffset> Left( + LHS->kind(), LHS->getName(), LHS->getLineNumber(), LHS->getOffset()); + std::tuple<std::string, StringRef, uint32_t, LVOffset> Right( + RHS->kind(), RHS->getName(), RHS->getLineNumber(), RHS->getOffset()); + return Left < Right; +} + +// Callback comparator based on multiple keys (First: Line). +LVSortValue llvm::logicalview::sortByLine(const LVObject *LHS, + const LVObject *RHS) { + // Order in which the object attributes are used for comparison: + // line number, name, kind, offset. + std::tuple<uint32_t, StringRef, std::string, LVOffset> Left( + LHS->getLineNumber(), LHS->getName(), LHS->kind(), LHS->getOffset()); + std::tuple<uint32_t, StringRef, std::string, LVOffset> Right( + RHS->getLineNumber(), RHS->getName(), RHS->kind(), RHS->getOffset()); + return Left < Right; +} + +// Callback comparator based on multiple keys (First: Name). +LVSortValue llvm::logicalview::sortByName(const LVObject *LHS, + const LVObject *RHS) { + // Order in which the object attributes are used for comparison: + // name, line number, kind, offset. + std::tuple<StringRef, uint32_t, std::string, LVOffset> Left( + LHS->getName(), LHS->getLineNumber(), LHS->kind(), LHS->getOffset()); + std::tuple<StringRef, uint32_t, std::string, LVOffset> Right( + RHS->getName(), RHS->getLineNumber(), RHS->kind(), RHS->getOffset()); + return Left < Right; +} + +LVSortFunction llvm::logicalview::getSortFunction() { + using LVSortInfo = std::map<LVSortMode, LVSortFunction>; + static LVSortInfo SortInfo = { + {LVSortMode::None, nullptr}, {LVSortMode::Kind, sortByKind}, + {LVSortMode::Line, sortByLine}, {LVSortMode::Name, sortByName}, + {LVSortMode::Offset, compareOffset}, + }; + + LVSortFunction SortFunction = nullptr; + LVSortInfo::iterator Iter = SortInfo.find(options().getSortMode()); + if (Iter != SortInfo.end()) + SortFunction = Iter->second; + return SortFunction; +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp new file mode 100644 index 000000000000..9fa1f28eb089 --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp @@ -0,0 +1,56 @@ +//===-- LVSupport.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 supporting functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include <iomanip> + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Support" + +// Perform the following transformations to the given 'Path': +// - all characters to lowercase. +// - '\\' into '/' (Platform independent). +// - '//' into '/' +std::string llvm::logicalview::transformPath(StringRef Path) { + std::string Name(Path); + std::transform(Name.begin(), Name.end(), Name.begin(), tolower); + std::replace(Name.begin(), Name.end(), '\\', '/'); + + // Remove all duplicate slashes. + size_t Pos = 0; + while ((Pos = Name.find("//", Pos)) != std::string::npos) + Name.erase(Pos, 1); + + return Name; +} + +// Convert the given 'Path' to lowercase and change any matching character +// from 'CharSet' into '_'. +// The characters in 'CharSet' are: +// '/', '\', '<', '>', '.', ':', '%', '*', '?', '|', '"', ' '. +std::string llvm::logicalview::flattenedFilePath(StringRef Path) { + std::string Name(Path); + std::transform(Name.begin(), Name.end(), Name.begin(), tolower); + + const char *CharSet = "/\\<>.:%*?|\" "; + char *Input = Name.data(); + while (Input && *Input) { + Input = strpbrk(Input, CharSet); + if (Input) + *Input++ = '_'; + }; + return Name; +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp new file mode 100644 index 000000000000..82633fbc6b2e --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp @@ -0,0 +1,449 @@ +//===-- LVSymbol.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 LVSymbol class. +// +//===----------------------------------------------------------------------===// + +#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" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Symbol" + +namespace { +const char *const KindCallSiteParameter = "CallSiteParameter"; +const char *const KindConstant = "Constant"; +const char *const KindInherits = "Inherits"; +const char *const KindMember = "Member"; +const char *const KindParameter = "Parameter"; +const char *const KindUndefined = "Undefined"; +const char *const KindUnspecified = "Unspecified"; +const char *const KindVariable = "Variable"; +} // end anonymous namespace + +// Return a string representation for the symbol kind. +const char *LVSymbol::kind() const { + const char *Kind = KindUndefined; + if (getIsCallSiteParameter()) + Kind = KindCallSiteParameter; + else if (getIsConstant()) + Kind = KindConstant; + else if (getIsInheritance()) + Kind = KindInherits; + else if (getIsMember()) + Kind = KindMember; + else if (getIsParameter()) + Kind = KindParameter; + else if (getIsUnspecified()) + Kind = KindUnspecified; + else if (getIsVariable()) + Kind = KindVariable; + return Kind; +} + +LVSymbolDispatch LVSymbol::Dispatch = { + {LVSymbolKind::IsCallSiteParameter, &LVSymbol::getIsCallSiteParameter}, + {LVSymbolKind::IsConstant, &LVSymbol::getIsConstant}, + {LVSymbolKind::IsInheritance, &LVSymbol::getIsInheritance}, + {LVSymbolKind::IsMember, &LVSymbol::getIsMember}, + {LVSymbolKind::IsParameter, &LVSymbol::getIsParameter}, + {LVSymbolKind::IsUnspecified, &LVSymbol::getIsUnspecified}, + {LVSymbolKind::IsVariable, &LVSymbol::getIsVariable}}; + +// Add a Location Entry. +void LVSymbol::addLocation(dwarf::Attribute Attr, LVAddress LowPC, + LVAddress HighPC, LVUnsigned SectionOffset, + uint64_t LocDescOffset, bool CallSiteLocation) { + if (!Locations) + Locations = new LVAutoLocations(); + + // Create the location entry. + CurrentLocation = new LVLocationSymbol(); + CurrentLocation->setParent(this); + CurrentLocation->setAttr(Attr); + if (CallSiteLocation) + CurrentLocation->setIsCallSite(); + CurrentLocation->addObject(LowPC, HighPC, SectionOffset, LocDescOffset); + Locations->push_back(CurrentLocation); + + // Mark the symbol as having location information. + setHasLocation(); +} + +// Add a Location Record. +void LVSymbol::addLocationOperands(LVSmall Opcode, uint64_t Operand1, + uint64_t Operand2) { + if (CurrentLocation) + CurrentLocation->addObject(Opcode, Operand1, Operand2); +} + +// Add a Location Entry. +void LVSymbol::addLocationConstant(dwarf::Attribute Attr, LVUnsigned Constant, + uint64_t LocDescOffset) { + // Create a Location Entry, with the global information. + addLocation(Attr, + /*LowPC=*/0, /*HighPC=*/-1, + /*SectionOffset=*/0, LocDescOffset); + + // Add records to Location Entry. + addLocationOperands(/*Opcode=*/LVLocationMemberOffset, + /*Operand1=*/Constant, /*Operand2=*/0); +} + +LVLocations::iterator LVSymbol::addLocationGap(LVLocations::iterator Pos, + LVAddress LowPC, + LVAddress HighPC) { + // Create a location entry for the gap. + LVLocation *Gap = new LVLocationSymbol(); + Gap->setParent(this); + Gap->setAttr(dwarf::DW_AT_location); + Gap->addObject(LowPC, HighPC, + /*section_offset=*/0, + /*locdesc_offset=*/0); + + LVLocations::iterator Iter = Locations->insert(Pos, Gap); + + // Add gap to Location Entry. + Gap->addObject(/*op=*/dwarf::DW_OP_hi_user, + /*opd1=*/0, /*opd2=*/0); + + // Mark the entry as a gap. + Gap->setIsGapEntry(); + + return Iter; +} + +void LVSymbol::fillLocationGaps() { + // The symbol has locations records. Fill gaps in the location list. + if (!getHasLocation() || !getFillGaps()) + return; + + // Get the parent range information and add dummy location entries. + const LVLocations *Ranges = getParentScope()->getRanges(); + if (!Ranges) + return; + + for (const LVLocation *Entry : *Ranges) { + LVAddress ParentLowPC = Entry->getLowerAddress(); + LVAddress ParentHighPC = Entry->getUpperAddress(); + + // Traverse the symbol locations and for each location contained in + // the current parent range, insert locations for any existing gap. + LVLocation *Location; + LVAddress LowPC = 0; + LVAddress Marker = ParentLowPC; + for (LVLocations::iterator Iter = Locations->begin(); + Iter != Locations->end(); ++Iter) { + Location = *Iter; + LowPC = Location->getLowerAddress(); + if (LowPC != Marker) { + // We have a gap at [Marker,LowPC - 1]. + Iter = addLocationGap(Iter, Marker, LowPC - 1); + ++Iter; + } + + // Move to the next item in the location list. + Marker = Location->getUpperAddress() + 1; + } + + // Check any gap at the end. + if (Marker < ParentHighPC) + // We have a gap at [Marker,ParentHighPC]. + addLocationGap(Locations->end(), Marker, ParentHighPC); + } +} + +// Get all the locations based on the valid function. +void LVSymbol::getLocations(LVLocations &LocationList, + LVValidLocation ValidLocation, bool RecordInvalid) { + if (!Locations) + return; + + for (LVLocation *Location : *Locations) { + // Add the invalid location object. + if (!(Location->*ValidLocation)() && RecordInvalid) + LocationList.push_back(Location); + } + + // Calculate coverage factor. + calculateCoverage(); +} + +void LVSymbol::getLocations(LVLocations &LocationList) const { + if (!Locations) + return; + + for (LVLocation *Location : *Locations) + LocationList.push_back(Location); +} + +// Calculate coverage factor. +void LVSymbol::calculateCoverage() { + if (!LVLocation::calculateCoverage(Locations, CoverageFactor, + CoveragePercentage)) { + LVScope *Parent = getParentScope(); + if (Parent->getIsInlinedFunction()) { + // For symbols representing the inlined function parameters and its + // variables, get the outer most parent that contains their location + // lower address. + // The symbol can have a set of non-contiguous locations. We are using + // only the first location entry to get the outermost parent. + // If no scope contains the location, assume its enclosing parent. + LVScope *Scope = + Parent->outermostParent(Locations->front()->getLowerAddress()); + if (Scope) + Parent = Scope; + } + unsigned CoverageParent = Parent->getCoverageFactor(); + // Get a percentage rounded to two decimal digits. This avoids + // implementation-defined rounding inside printing functions. + CoveragePercentage = + CoverageParent + ? rint((double(CoverageFactor) / CoverageParent) * 100.0 * 100.0) / + 100.0 + : 0; + // Record invalid coverage entry. + if (options().getWarningCoverages() && CoveragePercentage > 100) + getReaderCompileUnit()->addInvalidCoverage(this); + } +} + +void LVSymbol::resolveName() { + if (getIsResolvedName()) + return; + setIsResolvedName(); + + LVElement::resolveName(); + + // Resolve any given pattern. + patterns().resolvePatternMatch(this); +} + +void LVSymbol::resolveReferences() { + // The symbols can have the following references to other elements: + // A Type: + // DW_AT_type -> Type or Scope + // DW_AT_import -> Type + // A Reference: + // DW_AT_specification -> Symbol + // DW_AT_abstract_origin -> Symbol + // DW_AT_extension -> Symbol + + // Resolve any referenced symbol. + LVSymbol *Reference = getReference(); + if (Reference) { + Reference->resolve(); + // Recursively resolve the symbol names. + resolveReferencesChain(); + } + + // Set the file/line information using the Debug Information entry. + setFile(Reference); + + // Resolve symbol type. + if (LVElement *Element = getType()) { + Element->resolve(); + + // In the case of demoted typedefs, use the underlying type. + if (Element->getIsTypedefReduced()) { + Element = Element->getType(); + Element->resolve(); + } + + // If the type is a template parameter, get its type, which can + // point to a type or scope, depending on the argument instance. + setGenericType(Element); + } + + // Resolve the variable associated type. + if (!getType() && Reference) + setType(Reference->getType()); +} + +StringRef LVSymbol::resolveReferencesChain() { + // If the symbol have a DW_AT_specification or DW_AT_abstract_origin, + // follow the chain to resolve the name from those references. + if (getHasReference() && !isNamed()) + setName(getReference()->resolveReferencesChain()); + + 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) + Location->printRaw(OS, Full); +} + +void LVSymbol::print(raw_ostream &OS, bool Full) const { + if (getIncludeInPrint() && getReader().doPrintSymbol(this)) { + getReaderCompileUnit()->incrementPrintedSymbols(); + LVElement::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVSymbol::printExtra(raw_ostream &OS, bool Full) const { + // Accessibility depends on the parent (class, structure). + uint32_t AccessCode = 0; + if (getIsMember() || getIsInheritance()) + AccessCode = getParentScope()->getIsClass() ? dwarf::DW_ACCESS_private + : dwarf::DW_ACCESS_public; + + const LVSymbol *Symbol = getIsInlined() ? Reference : this; + std::string Attributes = + Symbol->getIsCallSiteParameter() + ? "" + : formatAttributes(Symbol->externalString(), + Symbol->accessibilityString(AccessCode), + virtualityString()); + + OS << formattedKind(Symbol->kind()) << " " << Attributes; + if (Symbol->getIsUnspecified()) + OS << formattedName(Symbol->getName()); + else { + if (Symbol->getIsInheritance()) + OS << Symbol->typeOffsetAsString() + << formattedNames(Symbol->getTypeQualifiedName(), + Symbol->typeAsString()); + else { + OS << formattedName(Symbol->getName()); + // Print any bitfield information. + if (uint32_t Size = getBitSize()) + OS << ":" << Size; + OS << " -> " << Symbol->typeOffsetAsString() + << formattedNames(Symbol->getTypeQualifiedName(), + Symbol->typeAsString()); + } + } + + // Print any initial value if any. + if (ValueIndex) + OS << " = " << formattedName(getValue()); + OS << "\n"; + + if (Full && options().getPrintFormatting()) { + if (getLinkageNameIndex()) + printLinkageName(OS, Full, const_cast<LVSymbol *>(this)); + if (LVSymbol *Reference = getReference()) + Reference->printReference(OS, Full, const_cast<LVSymbol *>(this)); + + // Print location information. + LVLocation::print(Locations, OS, Full); + } +} diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp new file mode 100644 index 000000000000..3d32c34ee02a --- /dev/null +++ b/llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp @@ -0,0 +1,524 @@ +//===-- LVType.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 LVType class. +// +//===----------------------------------------------------------------------===// + +#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" + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Type" + +namespace { +const char *const KindBaseType = "BaseType"; +const char *const KindConst = "Const"; +const char *const KindEnumerator = "Enumerator"; +const char *const KindImport = "Import"; +const char *const KindPointer = "Pointer"; +const char *const KindPointerMember = "PointerMember"; +const char *const KindReference = "Reference"; +const char *const KindRestrict = "Restrict"; +const char *const KindRvalueReference = "RvalueReference"; +const char *const KindSubrange = "Subrange"; +const char *const KindTemplateTemplate = "TemplateTemplate"; +const char *const KindTemplateType = "TemplateType"; +const char *const KindTemplateValue = "TemplateValue"; +const char *const KindTypeAlias = "TypeAlias"; +const char *const KindUndefined = "Undefined"; +const char *const KindUnaligned = "Unaligned"; +const char *const KindUnspecified = "Unspecified"; +const char *const KindVolatile = "Volatile"; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// DWARF Type. +//===----------------------------------------------------------------------===// +// Return a string representation for the type kind. +const char *LVType::kind() const { + const char *Kind = KindUndefined; + if (getIsBase()) + Kind = KindBaseType; + else if (getIsConst()) + Kind = KindConst; + else if (getIsEnumerator()) + Kind = KindEnumerator; + else if (getIsImport()) + Kind = KindImport; + else if (getIsPointerMember()) + Kind = KindPointerMember; + else if (getIsPointer()) + Kind = KindPointer; + else if (getIsReference()) + Kind = KindReference; + else if (getIsRestrict()) + Kind = KindRestrict; + else if (getIsRvalueReference()) + Kind = KindRvalueReference; + else if (getIsSubrange()) + Kind = KindSubrange; + else if (getIsTemplateTypeParam()) + Kind = KindTemplateType; + else if (getIsTemplateValueParam()) + Kind = KindTemplateValue; + else if (getIsTemplateTemplateParam()) + Kind = KindTemplateTemplate; + else if (getIsTypedef()) + Kind = KindTypeAlias; + else if (getIsUnaligned()) + Kind = KindUnaligned; + else if (getIsUnspecified()) + Kind = KindUnspecified; + else if (getIsVolatile()) + Kind = KindVolatile; + return Kind; +} + +LVTypeDispatch LVType::Dispatch = { + {LVTypeKind::IsBase, &LVType::getIsBase}, + {LVTypeKind::IsConst, &LVType::getIsConst}, + {LVTypeKind::IsEnumerator, &LVType::getIsEnumerator}, + {LVTypeKind::IsImport, &LVType::getIsImport}, + {LVTypeKind::IsImportDeclaration, &LVType::getIsImportDeclaration}, + {LVTypeKind::IsImportModule, &LVType::getIsImportModule}, + {LVTypeKind::IsPointer, &LVType::getIsPointer}, + {LVTypeKind::IsPointerMember, &LVType::getIsPointerMember}, + {LVTypeKind::IsReference, &LVType::getIsReference}, + {LVTypeKind::IsRestrict, &LVType::getIsRestrict}, + {LVTypeKind::IsRvalueReference, &LVType::getIsRvalueReference}, + {LVTypeKind::IsSubrange, &LVType::getIsSubrange}, + {LVTypeKind::IsTemplateParam, &LVType::getIsTemplateParam}, + {LVTypeKind::IsTemplateTemplateParam, &LVType::getIsTemplateTemplateParam}, + {LVTypeKind::IsTemplateTypeParam, &LVType::getIsTemplateTypeParam}, + {LVTypeKind::IsTemplateValueParam, &LVType::getIsTemplateValueParam}, + {LVTypeKind::IsTypedef, &LVType::getIsTypedef}, + {LVTypeKind::IsUnaligned, &LVType::getIsUnaligned}, + {LVTypeKind::IsUnspecified, &LVType::getIsUnspecified}, + {LVTypeKind::IsVolatile, &LVType::getIsVolatile}}; + +void LVType::resolveReferences() { + // Some DWARF tags are the representation of types. However, we associate + // some of them to scopes. The ones associated with types, do not have + // any reference tags, such as DW_AT_specification, DW_AT_abstract_origin, + // DW_AT_extension. + + // Set the file/line information using the Debug Information entry. + setFile(/*Reference=*/nullptr); + + if (LVElement *Element = getType()) + Element->resolve(); +} + +void LVType::resolveName() { + if (getIsResolvedName()) + return; + setIsResolvedName(); + + // The templates are recorded as normal DWARF objects relationships; + // the template parameters are preserved to show the types used during + // the instantiation; however if a compare have been requested, those + // parameters needs to be resolved, so no conflicts are generated. + // The following DWARF illustrates this issue: + // + // a) Template Parameters are preserved: + // {Class} 'ConstArray<AtomTable>' + // {Inherits} -> 'ArrayBase' + // {TemplateType} 'taTYPE' -> 'AtomTable' + // {Member} 'mData' -> '* taTYPE' + // + // b) Template Parameters are resolved: + // {Class} 'ConstArray<AtomTable>' + // {Inherits} -> 'ArrayBase' + // {TemplateType} 'taTYPE' -> 'AtomTable' + // {Member} 'mData' -> '* AtomTable' + // + // In (b), the {Member} type have been resolved to use the real type. + + LVElement *BaseType = getType(); + if (BaseType && options().getAttributeArgument()) + if (BaseType->isTemplateParam()) + BaseType = BaseType->getType(); + + if (BaseType && !BaseType->getIsResolvedName()) + BaseType->resolveName(); + resolveFullname(BaseType, getName()); + + // In the case of unnamed types, try to generate a name for it, using + // the parents name and the line information. Ignore the template parameters. + if (!isNamed() && !getIsTemplateParam()) + generateName(); + + LVElement::resolveName(); + + // Resolve any given pattern. + patterns().resolvePatternMatch(this); +} + +StringRef LVType::resolveReferencesChain() { + // The types do not have a DW_AT_specification or DW_AT_abstract_origin + // reference. Just return the type name. + 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))) { + getReaderCompileUnit()->incrementPrintedTypes(); + LVElement::print(OS, Full); + printExtra(OS, Full); + } +} + +void LVType::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF typedef. +//===----------------------------------------------------------------------===// +// Return the underlying type for a typedef, which can be a type or scope. +LVElement *LVTypeDefinition::getUnderlyingType() { + LVElement *BaseType = getTypeAsScope(); + if (BaseType) + // Underlying type is a scope. + return BaseType; + + LVType *Type = getTypeAsType(); + assert(Type && "Type definition does not have a type."); + + BaseType = Type; + while (Type->getIsTypedef()) { + BaseType = Type->getTypeAsScope(); + if (BaseType) + // Underlying type is a scope. + return BaseType; + + Type = Type->getTypeAsType(); + if (Type) + BaseType = Type; + } + + return BaseType; +} + +void LVTypeDefinition::resolveExtra() { + // Set the reference to the typedef type. + if (options().getAttributeUnderlying()) { + setUnderlyingType(getUnderlyingType()); + setIsTypedefReduced(); + if (LVElement *Type = getType()) { + Type->resolveName(); + resolveFullname(Type); + } + } + + // For the case of typedef'd anonymous structures: + // typedef struct { ... } Name; + // Propagate the typedef name to the anonymous structure. + LVScope *Aggregate = getTypeAsScope(); + if (Aggregate && Aggregate->getIsAnonymous()) + 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() + << formattedName((getType() ? getType()->getName() : "")) << "\n"; +} + +//===----------------------------------------------------------------------===// +// 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"; +} + +//===----------------------------------------------------------------------===// +// 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()); + + OS << formattedKind(kind()) << " " << typeOffsetAsString() << Attributes + << formattedName((getType() ? getType()->getName() : "")) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DWARF Template parameter holder (type or param). +//===----------------------------------------------------------------------===// +LVTypeParam::LVTypeParam() : LVType() { + options().getAttributeTypename() ? setIncludeInPrint() + : resetIncludeInPrint(); +} + +// Encode the specific template argument. +void LVTypeParam::encodeTemplateArgument(std::string &Name) const { + // The incoming type is a template parameter; we have 3 kinds of parameters: + // - type parameter: resolve the instance (type); + // - value parameter: resolve the constant value + // - template parameter: resolve the name of the template. + // If the parameter type is a template instance (STL sample), we need to + // expand the type (template template case). For the following variable + // declarations: + // std::type<float> a_float; + // std::type<int> a_int; + // We must generate names like: + // "std::type<float,std::less<float>,std::allocator<float>,false>" + // "std::type<int,std::less<int>,std::allocator<int>,false>" + // Instead of the incomplete names: + // "type<float,less,allocator,false>" + // "type<int,less,allocator,false>" + + if (getIsTemplateTypeParam()) { + // Get the type instance recorded in the template type; it can be a + // reference to a type or to a scope. + + if (getIsKindType()) { + // The argument types always are qualified. + Name.append(std::string(getTypeQualifiedName())); + + LVType *ArgType = getTypeAsType(); + // For template arguments that are typedefs, use the underlying type, + // which can be a type or scope. + if (ArgType->getIsTypedef()) { + LVObject *BaseType = ArgType->getUnderlyingType(); + Name.append(std::string(BaseType->getName())); + } else { + Name.append(std::string(ArgType->getName())); + } + } else { + if (getIsKindScope()) { + LVScope *ArgScope = getTypeAsScope(); + // If the scope is a template, we have to resolve that template, + // by recursively traversing its arguments. + if (ArgScope->getIsTemplate()) + ArgScope->encodeTemplateArguments(Name); + else { + // The argument types always are qualified. + Name.append(std::string(getTypeQualifiedName())); + Name.append(std::string(ArgScope->getName())); + } + } + } + } else + // Template value parameter or template template parameter. + 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(); + + // Depending on the type of parameter, the print includes different + // information: type, value or reference to a template. + if (getIsTemplateTypeParam()) { + OS << formattedNames(getTypeQualifiedName(), getTypeName()) << "\n"; + return; + } + if (getIsTemplateValueParam()) { + OS << formattedName(getValue()) << " " << formattedName(getName()) << "\n"; + return; + } + if (getIsTemplateTemplateParam()) + OS << formattedName(getValue()) << "\n"; +} + +//===----------------------------------------------------------------------===// +// DW_TAG_subrange_type +//===----------------------------------------------------------------------===// +void LVTypeSubrange::resolveExtra() { + // There are 2 cases to represent the bounds information for an array: + // 1) DW_TAG_subrange_type + // DW_AT_type --> ref_type (type of count) + // DW_AT_count --> value (number of elements in subrange) + + // 2) DW_TAG_subrange_type + // DW_AT_lower_bound --> value + // DW_AT_upper_bound --> value + + // The idea is to represent the bounds as a string, depending on the format: + // 1) [count] + // 2) [lower..upper] + + // Subrange information. + std::string String; + + // Check if we have DW_AT_count subrange style. + if (getIsSubrangeCount()) + // Get count subrange value. Assume 0 if missing. + raw_string_ostream(String) << "[" << getCount() << "]"; + else + raw_string_ostream(String) + << "[" << getLowerBound() << ".." << getUpperBound() << "]"; + + 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"; +} |