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