Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVElement.h @@ -16,34 +16,320 @@ #include "llvm/DebugInfo/LogicalView/Core/LVObject.h" #include "llvm/DebugInfo/LogicalView/Core/LVStringPool.h" +#include "llvm/Support/Casting.h" #include namespace llvm { namespace logicalview { +// RTTI Subclasses ID. +enum class LVSubclassID : unsigned char { + LV_ELEMENT, + LV_LINE_FIRST, + LV_LINE, + LV_LINE_DEBUG, + LV_LINE_ASSEMBLER, + LV_LINE_LAST, + lV_SCOPE_FIRST, + LV_SCOPE, + LV_SCOPE_AGGREGATE, + LV_SCOPE_ALIAS, + LV_SCOPE_ARRAY, + LV_SCOPE_COMPILE_UNIT, + LV_SCOPE_ENUMERATION, + LV_SCOPE_FORMAL_PACK, + LV_SCOPE_FUNCTION, + LV_SCOPE_FUNCTION_INLINED, + LV_SCOPE_FUNCTION_TYPE, + LV_SCOPE_NAMESPACE, + LV_SCOPE_ROOT, + LV_SCOPE_TEMPLATE_PACK, + LV_SCOPE_LAST, + LV_SYMBOL_FIRST, + LV_SYMBOL, + LV_SYMBOL_LAST, + LV_TYPE_FIRST, + LV_TYPE, + LV_TYPE_DEFINITION, + LV_TYPE_ENUMERATOR, + LV_TYPE_IMPORT, + LV_TYPE_PARAM, + LV_TYPE_SUBRANGE, + LV_TYPE_LAST +}; + enum class LVElementKind { Discarded, Global, Optimized, LastEntry }; using LVElementKindSet = std::set; class LVElement : public LVObject { + enum class Property { + IsLine, // A logical line. + IsScope, // A logical scope. + IsSymbol, // A logical symbol. + IsType, // A logical type. + IsEnumClass, + IsExternal, + HasType, + HasAugmentedName, + IsTypedefReduced, + IsArrayResolved, + IsMemberPointerResolved, + IsTemplateResolved, + IsInlined, + IsInlinedAbstract, + InvalidFilename, + HasReference, + HasReferenceAbstract, + HasReferenceExtension, + HasReferenceSpecification, + QualifiedResolved, + IncludeInPrint, + IsStatic, + TransformName, + IsScoped, // CodeView local type. + IsNested, // CodeView nested type. + IsScopedAlready, // CodeView nested type inserted in correct scope. + IsArtificial, + IsReferencedType, + IsSystem, + OffsetFromTypeIndex, + IsAnonymous, + LastEntry + }; + // Typed bitvector with properties for this element. + LVProperties Properties; + + /// RTTI. + const LVSubclassID SubclassID; + // Indexes in the String Pool. size_t NameIndex = 0; + size_t QualifiedNameIndex = 0; size_t FilenameIndex = 0; + uint16_t AccessibilityCode : 2; // DW_AT_accessibility. + uint16_t InlineCode : 2; // DW_AT_inline. + uint16_t VirtualityCode : 2; // DW_AT_virtuality. + + // The given Specification points to an element that is connected via the + // DW_AT_specification, DW_AT_abstract_origin or DW_AT_extension attribute. + void setFileLine(LVElement *Specification); + + // Get the qualified name that include its parents name. + void resolveQualifiedName(); + +protected: + // Type of this element. + LVElement *ElementType = nullptr; + + // Print the FileName Index. + void printFileIndex(raw_ostream &OS, bool Full = true) const override; + public: + LVElement(LVSubclassID ID) + : LVObject(), SubclassID(ID), AccessibilityCode(0), InlineCode(0), + VirtualityCode(0) {} + LVElement(const LVElement &) = delete; + LVElement &operator=(const LVElement &) = delete; + virtual ~LVElement() = default; + + LVSubclassID getSubclassID() const { return SubclassID; } + + PROPERTY(Property, IsLine); + PROPERTY(Property, IsScope); + PROPERTY(Property, IsSymbol); + PROPERTY(Property, IsType); + PROPERTY(Property, IsEnumClass); + PROPERTY(Property, IsExternal); + PROPERTY(Property, HasType); + PROPERTY(Property, HasAugmentedName); + PROPERTY(Property, IsTypedefReduced); + PROPERTY(Property, IsArrayResolved); + PROPERTY(Property, IsMemberPointerResolved); + PROPERTY(Property, IsTemplateResolved); + PROPERTY(Property, IsInlined); + PROPERTY(Property, IsInlinedAbstract); + PROPERTY(Property, InvalidFilename); + PROPERTY(Property, HasReference); + PROPERTY(Property, HasReferenceAbstract); + PROPERTY(Property, HasReferenceExtension); + PROPERTY(Property, HasReferenceSpecification); + PROPERTY(Property, QualifiedResolved); + PROPERTY(Property, IncludeInPrint); + PROPERTY(Property, IsStatic); + PROPERTY(Property, TransformName); + PROPERTY(Property, IsScoped); + PROPERTY(Property, IsNested); + PROPERTY(Property, IsScopedAlready); + PROPERTY(Property, IsArtificial); + PROPERTY(Property, IsReferencedType); + PROPERTY(Property, IsSystem); + PROPERTY(Property, OffsetFromTypeIndex); + PROPERTY(Property, IsAnonymous); + bool isNamed() const override { return NameIndex != 0; } + bool isTyped() const override { return ElementType != nullptr; } + bool isFiled() const override { return FilenameIndex != 0; } + + // The Element class type can point to a Type or Scope. + bool getIsKindType() const { return ElementType && ElementType->getIsType(); } + bool getIsKindScope() const { + return ElementType && ElementType->getIsScope(); + } StringRef getName() const override { return getStringPool().getString(NameIndex); } + void setName(StringRef ElementName) override; // Get pathname associated with the Element. StringRef getPathname() const { return getStringPool().getString(getFilenameIndex()); } + // Set filename associated with the Element. + void setFilename(StringRef Filename); + + // Set the Element qualified name. + void setQualifiedName(StringRef Name) { + QualifiedNameIndex = getStringPool().getIndex(Name); + } + StringRef getQualifiedName() const { + return getStringPool().getString(QualifiedNameIndex); + } + + size_t getNameIndex() const { return NameIndex; } + size_t getQualifiedNameIndex() const { return QualifiedNameIndex; } + // Element type name. StringRef getTypeName() const; + + virtual StringRef getProducer() const { return StringRef(); } + virtual void setProducer(StringRef ProducerName) {} + + virtual bool isCompileUnit() const { return false; } + virtual bool isRoot() const { return false; } + + virtual void setReference(LVElement *Element) {} + virtual void setReference(LVScope *Scope) {} + virtual void setReference(LVSymbol *Symbol) {} + virtual void setReference(LVType *Type) {} + + virtual void setLinkageName(StringRef LinkageName) {} + virtual StringRef getLinkageName() const { return StringRef(); } + virtual size_t getLinkageNameIndex() const { return 0; } + + virtual uint32_t getCallLineNumber() const { return 0; } + virtual void setCallLineNumber(uint32_t Number) {} + virtual size_t getCallFilenameIndex() const { return 0; } + virtual void setCallFilenameIndex(size_t Index) {} size_t getFilenameIndex() const { return FilenameIndex; } + void setFilenameIndex(size_t Index) { FilenameIndex = Index; } + + // Set the File location for the Element. + void setFile(LVElement *Reference = nullptr); + + virtual bool isBase() const { return false; } + virtual bool isTemplateParam() const { return false; } + + virtual uint32_t getBitSize() const { return 0; } + virtual void setBitSize(uint32_t Size) {} + + virtual int64_t getCount() const { return 0; } + virtual void setCount(int64_t Value) {} + virtual int64_t getLowerBound() const { return 0; } + virtual void setLowerBound(int64_t Value) {} + virtual int64_t getUpperBound() const { return 0; } + virtual void setUpperBound(int64_t Value) {} + virtual std::pair getBounds() const { return {}; } + virtual void setBounds(unsigned Lower, unsigned Upper) {} + + // Access DW_AT_GNU_discriminator attribute. + virtual uint32_t getDiscriminator() const { return 0; } + virtual void setDiscriminator(uint32_t Value) {} + + // Process the values for a DW_TAG_enumerator. + virtual std::string getValue() const { return {}; } + virtual void setValue(StringRef Value) {} + virtual size_t getValueIndex() const { return 0; } + + // DWARF Accessibility Codes. + uint32_t getAccessibilityCode() const { return AccessibilityCode; } + void setAccessibilityCode(uint32_t Access) { AccessibilityCode = Access; } + StringRef + accessibilityString(uint32_t Access = dwarf::DW_ACCESS_private) const; + + // DWARF Inline Codes. + uint32_t getInlineCode() const { return InlineCode; } + void setInlineCode(uint32_t Code) { InlineCode = Code; } + StringRef inlineCodeString(uint32_t Code) const; + + // DWARF Virtuality Codes. + uint32_t getVirtualityCode() const { return VirtualityCode; } + void setVirtualityCode(uint32_t Virtuality) { VirtualityCode = Virtuality; } + StringRef + virtualityString(uint32_t Virtuality = dwarf::DW_VIRTUALITY_none) const; + + // DWARF Extern Codes. + StringRef externalString() const; + + LVElement *getType() const { return ElementType; } + LVType *getTypeAsType() const; + LVScope *getTypeAsScope() const; + + void setType(LVElement *Element = nullptr) { + ElementType = Element; + if (Element) { + setHasType(); + Element->setIsReferencedType(); + } + } + + // Set the type for the element, handling template parameters. + void setGenericType(LVElement *Element); + + StringRef getTypeQualifiedName() const { + return ElementType ? ElementType->getQualifiedName() : ""; + } + + StringRef typeAsString() const; + std::string typeOffsetAsString() const; + std::string discriminatorAsString() const; + + LVScope *traverseParents(LVScopeGetFunction GetFunction) const; + + LVScope *getFunctionParent() const; + virtual LVScope *getCompileUnitParent() const; + + // Print any referenced element. + void printReference(raw_ostream &OS, bool Full, LVElement *Parent) const; + + // Print the linkage name (Symbols and functions). + void printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent, + LVScope *Scope) const; + void printLinkageName(raw_ostream &OS, bool Full, LVElement *Parent) const; + + // Generate the full name for the Element. + void resolveFullname(LVElement *BaseType, StringRef Name = emptyString()); + + // Generate a name for unnamed elements. + void generateName(std::string &Prefix) const; + void generateName(); + + virtual bool removeElement(LVElement *Element) { return false; } + virtual void updateLevel(LVScope *Parent, bool Moved = false); + + // During the parsing of the debug information, the logical elements are + // created with information extracted from its description entries (DIE). + // But they are not complete for the logical view concept. A second pass + // is executed in order to collect their additional information. + // The following functions 'resolve' some of their properties, such as + // name, references, parents, extra information based on the element kind. + virtual void resolve(); + virtual void resolveExtra() {} + virtual void resolveName(); + virtual void resolveReferences() {} + void resolveParents(); }; } // end namespace logicalview Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVLine.h @@ -34,6 +34,100 @@ }; using LVLineKindSet = std::set; +// Class to represent a logical line. +class LVLine : public LVElement { + // Typed bitvector with kinds for this line. + LVProperties Kinds; + +public: + LVLine() : LVElement(LVSubclassID::LV_LINE) { + setIsLine(); + setIncludeInPrint(); + } + LVLine(const LVLine &) = delete; + LVLine &operator=(const LVLine &) = delete; + virtual ~LVLine() = default; + + static bool classof(const LVElement *Element) { + return Element->getSubclassID() == LVSubclassID::LV_LINE; + } + + KIND(LVLineKind, IsBasicBlock); + KIND(LVLineKind, IsDiscriminator); + KIND(LVLineKind, IsEndSequence); + KIND(LVLineKind, IsEpilogueBegin); + KIND(LVLineKind, IsLineDebug); + KIND(LVLineKind, IsLineAssembler); + KIND(LVLineKind, IsNewStatement); + KIND(LVLineKind, IsPrologueEnd); + KIND(LVLineKind, IsAlwaysStepInto); + KIND(LVLineKind, IsNeverStepInto); + + const char *kind() const override; + + // Use the offset to store the line address. + uint64_t getAddress() const { return getOffset(); } + void setAddress(uint64_t address) { setOffset(address); } + + // String used for printing objects with no line number. + std::string noLineAsString(bool ShowZero = false) const override; + + // Line number for display; in the case of Inlined Functions, we use the + // DW_AT_call_line attribute; otherwise use DW_AT_decl_line attribute. + std::string lineNumberAsString(bool ShowZero = false) const override { + return lineAsString(getLineNumber(), getDiscriminator(), ShowZero); + } + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override {} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +// Class to represent a DWARF line record object. +class LVLineDebug final : public LVLine { + // Discriminator value (DW_LNE_set_discriminator). The DWARF standard + // defines the discriminator as an unsigned LEB128 integer. + uint32_t Discriminator = 0; + +public: + LVLineDebug() : LVLine() { setIsLineDebug(); } + LVLineDebug(const LVLineDebug &) = delete; + LVLineDebug &operator=(const LVLineDebug &) = delete; + ~LVLineDebug() = default; + + // Additional line information. It includes attributes that describes + // states in the machine instructions (basic block, end prologue, etc). + std::string statesInfo(bool Formatted) const; + + // Access DW_LNE_set_discriminator attribute. + uint32_t getDiscriminator() const override { return Discriminator; } + void setDiscriminator(uint32_t Value) override { + Discriminator = Value; + setIsDiscriminator(); + } + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent an assembler line extracted from the text section. +class LVLineAssembler final : public LVLine { +public: + LVLineAssembler() : LVLine() { setIsLineAssembler(); } + LVLineAssembler(const LVLineAssembler &) = delete; + LVLineAssembler &operator=(const LVLineAssembler &) = delete; + ~LVLineAssembler() = default; + + // Print blanks as the line number. + std::string noLineAsString(bool ShowZero) const override { + return std::string(8, ' '); + }; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + } // end namespace logicalview } // end namespace llvm Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h @@ -14,17 +14,164 @@ #ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVOBJECT_H #define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVOBJECT_H +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/LogicalView/Core/LVSupport.h" +#include +#include +#include #include +namespace llvm { +namespace dwarf { +// Support for CodeView ModifierOptions::Unaligned. +constexpr Tag DW_TAG_unaligned = Tag(dwarf::DW_TAG_hi_user + 1); +} // namespace dwarf +} // namespace llvm + namespace llvm { namespace logicalview { +using LVAddress = uint64_t; using LVHalf = uint16_t; +using LVLevel = uint32_t; using LVOffset = uint64_t; +using LVSigned = int64_t; +using LVUnsigned = uint64_t; +using LVSmall = uint8_t; + +class LVElement; +class LVLine; +class LVLocation; +class LVLocationSymbol; +class LVObject; +class LVOperation; +class LVScope; +class LVSymbol; +class LVType; + +class LVOptions; +class LVPatterns; + +StringRef typeNone(); +StringRef typeVoid(); +StringRef typeInt(); +StringRef typeUnknown(); +StringRef emptyString(); + +using LVElementSetFunction = void (LVElement::*)(); +using LVElementGetFunction = bool (LVElement::*)() const; +using LVLineSetFunction = void (LVLine::*)(); +using LVLineGetFunction = bool (LVLine::*)() const; +using LVObjectSetFunction = void (LVObject::*)(); +using LVObjectGetFunction = bool (LVObject::*)() const; +using LVScopeSetFunction = void (LVScope::*)(); +using LVScopeGetFunction = bool (LVScope::*)() const; +using LVSymbolSetFunction = void (LVSymbol::*)(); +using LVSymbolGetFunction = bool (LVSymbol::*)() const; +using LVTypeSetFunction = void (LVType::*)(); +using LVTypeGetFunction = bool (LVType::*)() const; + +// The LVScope class represents a logical scope and uses vectors to store its +// children, which are pointers to other allocated logical elements (types, +// symbols, lines, scopes, ranges). On destruction, we have to traverse each +// vector and destroy its elements. The other case is LVSymbol. +// These definitions are intended to be used by the LVScope and LVSymbol +// to support automatic vector cleanup. +using LVAutoLines = LVAutoSmallVector; +using LVAutoLocations = LVAutoSmallVector; +using LVAutoOperations = LVAutoSmallVector; +using LVAutoScopes = LVAutoSmallVector; +using LVAutoSymbols = LVAutoSmallVector; +using LVAutoTypes = LVAutoSmallVector; + +// These definitions are intended to be used when the vector will be used +// just a container, with no automatic destruction. +using LVElements = SmallVector; +using LVLines = SmallVector; +using LVLocations = SmallVector; +using LVOperations = SmallVector; +using LVScopes = SmallVector; +using LVSymbols = SmallVector; +using LVTypes = SmallVector; + +using LVOffsets = SmallVector; + +const LVAddress MaxAddress = std::numeric_limits::max(); + +enum class LVBinaryType { NONE, ELF, COFF }; + +// Keep counters of objects. +struct LVCounter { + unsigned Lines = 0; + unsigned Scopes = 0; + unsigned Symbols = 0; + unsigned Types = 0; + void reset() { + Lines = 0; + Scopes = 0; + Symbols = 0; + Types = 0; + } +}; class LVObject { + enum class Property { + IsLocation, // Location. + IsGlobalReference, // This object is being referenced from another CU. + IsGeneratedName, // The Object name was generated. + IsResolved, // Object has been resolved. + IsResolvedName, // Object name has been resolved. + IsDiscarded, // Object has been stripped by the linker. + IsOptimized, // Object has been optimized by the compiler. + IsAdded, // Object has been 'added'. + IsMatched, // Object has been matched to a given pattern. + IsMissing, // Object is 'missing'. + IsMissingLink, // Object is indirectly 'missing'. + IsInCompare, // In 'compare' mode. + IsFileFromReference, // File ID from specification. + IsLineFromReference, // Line No from specification. + HasMoved, // The object was moved from 'target' to 'reference'. + HasPattern, // The object has a pattern. + IsFinalized, // CodeView object is finalized. + IsReferenced, // CodeView object being referenced. + HasCodeViewLocation, // CodeView object with debug location. + LastEntry + }; + // Typed bitvector with properties for this object. + LVProperties Properties; + LVOffset Offset = 0; + uint32_t LineNumber = 0; + LVLevel ScopeLevel = 0; + union { + dwarf::Tag Tag; + dwarf::Attribute Attr; + LVSmall Opcode; + } TagAttrOpcode = {dwarf::DW_TAG_null}; + + // The parent of this object (nullptr if the root scope). For locations, + // the parent is a symbol object; otherwise it is a scope object. + union { + LVElement *Element; + LVScope *Scope; + LVSymbol *Symbol; + } Parent = {nullptr}; + + // We do not support any object duplication, as they are created by parsing + // the debug information. There is only the case where we need a very basic + // object, to manipulate its offset, line number and scope level. Allow the + // copy constructor to create that object; it is used to print a reference + // to another object and in the case of templates, to print its encoded args. + LVObject(const LVObject &Object) { + Properties = Object.Properties; + Offset = Object.Offset; + LineNumber = Object.LineNumber; + ScopeLevel = Object.ScopeLevel; + TagAttrOpcode = Object.TagAttrOpcode; + Parent = Object.Parent; + } protected: // Get a string representation for the given number and discriminator. @@ -40,15 +187,119 @@ virtual void printFileIndex(raw_ostream &OS, bool Full = true) const {} public: - // True if the scope has been named. + LVObject() = default; + LVObject &operator=(const LVObject &) = delete; + virtual ~LVObject() = default; + + PROPERTY(Property, IsLocation); + PROPERTY(Property, IsGlobalReference); + PROPERTY(Property, IsGeneratedName); + PROPERTY(Property, IsResolved); + PROPERTY(Property, IsResolvedName); + PROPERTY(Property, IsDiscarded); + PROPERTY(Property, IsOptimized); + PROPERTY(Property, IsAdded); + PROPERTY(Property, IsMatched); + PROPERTY(Property, IsMissing); + PROPERTY(Property, IsMissingLink); + PROPERTY(Property, IsInCompare); + PROPERTY(Property, IsFileFromReference); + PROPERTY(Property, IsLineFromReference); + PROPERTY(Property, HasMoved); + PROPERTY(Property, HasPattern); + PROPERTY(Property, IsFinalized); + PROPERTY(Property, IsReferenced); + PROPERTY(Property, HasCodeViewLocation); + + // True if the scope has been named or typed or with line number. virtual bool isNamed() const { return false; } + virtual bool isTyped() const { return false; } + virtual bool isFiled() const { return false; } + bool isLined() const { return LineNumber != 0; } + + // DWARF tag, attribute or expression opcode. + dwarf::Tag getTag() const { return TagAttrOpcode.Tag; } + void setTag(dwarf::Tag Tag) { TagAttrOpcode.Tag = Tag; } + dwarf::Attribute getAttr() const { return TagAttrOpcode.Attr; } + void setAttr(dwarf::Attribute Attr) { TagAttrOpcode.Attr = Attr; } + LVSmall getOpcode() const { return TagAttrOpcode.Opcode; } + void setOpcode(LVSmall Opcode) { TagAttrOpcode.Opcode = Opcode; } // DIE offset. LVOffset getOffset() const { return Offset; } + void setOffset(LVOffset DieOffset) { Offset = DieOffset; } + + // Level where this object is located. + LVLevel getLevel() const { return ScopeLevel; } + void setLevel(LVLevel Level) { ScopeLevel = Level; } virtual StringRef getName() const { return StringRef(); } + virtual void setName(StringRef ObjectName) {} + + LVElement *getParent() const { + assert((!Parent.Element || + (Parent.Element && static_cast(Parent.Element))) && + "Invalid element"); + return Parent.Element; + } + LVScope *getParentScope() const { + assert((!Parent.Scope || + (Parent.Scope && static_cast(Parent.Scope))) && + "Invalid scope"); + return Parent.Scope; + } + LVSymbol *getParentSymbol() const { + assert((!Parent.Symbol || + (Parent.Symbol && static_cast(Parent.Symbol))) && + "Invalid symbol"); + return Parent.Symbol; + } + void setParent(LVScope *Scope); + void setParent(LVSymbol *Symbol); + void resetParent() { Parent = {nullptr}; } + uint32_t getLineNumber() const { return LineNumber; } + void setLineNumber(uint32_t Number) { LineNumber = Number; } + + virtual const char *kind() const { return nullptr; } + + std::string indentAsString() const; + std::string indentAsString(LVLevel Level) const; + + // String used as padding for printing objects with no line number. + virtual std::string noLineAsString(bool ShowZero) const; + + // Line number for display; in the case of inlined functions, we use the + // DW_AT_call_line attribute; otherwise use DW_AT_decl_line attribute. + virtual std::string lineNumberAsString(bool ShowZero = false) const { + return lineAsString(getLineNumber(), 0, ShowZero); + } std::string lineNumberAsStringStripped(bool ShowZero = false) const; + + // This function prints the logical view to an output stream. + // Split: Prints the compilation unit view to a file. + // Match: Prints the object only if it satisfies the patterns collected + // from the command line. See the '--select' option. + // Print: Print the object only if satisfies the conditions specified by + // the different '--print' options. + // Full: Prints full information for objects representing debug locations, + // aggregated scopes, compile unit, functions and namespaces. + virtual Error doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, + bool Full = true) const; + void printAttributes(raw_ostream &OS, bool Full = true) const; + void printAttributes(raw_ostream &OS, bool Full, StringRef Name, + LVObject *Parent, StringRef Value, + bool UseQuotes = false, bool PrintRef = false) const; + + // Prints the common information for an object (name, type, etc). + virtual void print(raw_ostream &OS, bool Full = true) const; + // Prints additional information for an object, depending on its kind + // (class attributes, debug ranges, files, directories, etc). + virtual void printExtra(raw_ostream &OS, bool Full = true) const {} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + virtual void dump() const { print(dbgs()); } +#endif }; } // end namespace logicalview Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h @@ -0,0 +1,164 @@ +//===-- LVReader.h ----------------------------------------------*- C++ -*-===// +// +// 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 file defines the LVReader class, which is used to describe a debug +// information reader. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVREADER_H +#define LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVREADER_H + +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include + +namespace llvm { +namespace logicalview { + +class LVScopeCompileUnit; +class LVObject; + +class LVSplitContext final { + std::unique_ptr OutputFile; + std::string Location; + +public: + LVSplitContext() = default; + LVSplitContext(const LVSplitContext &) = delete; + LVSplitContext &operator=(const LVSplitContext &) = delete; + ~LVSplitContext() = default; + + Error createSplitFolder(StringRef Where); + std::error_code open(std::string Name, std::string Extension, + raw_ostream &OS); + void close() { + if (OutputFile) { + OutputFile->os().close(); + OutputFile = nullptr; + } + } + + std::string getLocation() const { return Location; } + raw_fd_ostream &os() { return OutputFile->os(); } +}; + +class LVReader { + LVBinaryType BinaryType; + + // Context used by '--output=split' command line option. + LVSplitContext SplitContext; + + // Compile Units DIE Offset => Scope. + using LVCompileUnits = std::map; + LVCompileUnits CompileUnits; + + // Create split folder. + Error createSplitFolder(); + bool OutputSplit = false; + +protected: + LVScopeRoot *Root = nullptr; + std::string InputFilename; + std::string FileFormatName; + ScopedPrinter &W; + raw_ostream &OS; + LVScopeCompileUnit *CompileUnit = nullptr; + + // Record Compilation Unit entry. + void addCompileUnitOffset(LVOffset Offset, LVScopeCompileUnit *CompileUnit) { + CompileUnits.emplace(Offset, CompileUnit); + } + + // Create the Scope Root. + virtual Error createScopes() { + Root = new LVScopeRoot(); + Root->setName(getFilename()); + if (options().getAttributeFormat()) + Root->setFileFormatName(FileFormatName); + return Error::success(); + } + + virtual Error printScopes(); + virtual Error printMatchedElements(bool UseMatchedElements); + virtual void sortScopes() {} + +public: + LVReader() = delete; + LVReader(StringRef InputFilename, StringRef FileFormatName, ScopedPrinter &W, + LVBinaryType BinaryType = LVBinaryType::NONE) + : BinaryType(BinaryType), OutputSplit(options().getOutputSplit()), + InputFilename(InputFilename), FileFormatName(FileFormatName), W(W), + OS(W.getOStream()) {} + LVReader(const LVReader &) = delete; + LVReader &operator=(const LVReader &) = delete; + virtual ~LVReader() { + if (Root) + delete Root; + } + + StringRef getFilename(LVObject *Object, size_t Index) const; + StringRef getFilename() const { return InputFilename; } + void setFilename(std::string Name) { InputFilename = std::move(Name); } + StringRef getFileFormatName() const { return FileFormatName; } + + raw_ostream &outputStream() { return OS; } + + bool isBinaryTypeNone() const { return BinaryType == LVBinaryType::NONE; } + bool isBinaryTypeELF() const { return BinaryType == LVBinaryType::ELF; } + bool isBinaryTypeCOFF() const { return BinaryType == LVBinaryType::COFF; } + + LVScopeCompileUnit *getCompileUnit() const { return CompileUnit; } + void setCompileUnit(LVScope *Scope) { + assert(Scope && Scope->isCompileUnit() && "Scope is not a compile unit"); + CompileUnit = static_cast(Scope); + } + + // Access to the scopes root. + LVScopeRoot *getScopesRoot() const { return Root; } + + Error doPrint(); + Error doLoad(); + + virtual bool isSystemEntry(LVElement *Element, StringRef Name = {}) { + return false; + }; + + // Access to split context. + LVSplitContext &getSplitContext() { return SplitContext; } + + // Conditions to print an object. + bool doPrintLine(const LVLine *Line) const { return true; } + bool doPrintScope(const LVScope *Scope) const { return true; } + bool doPrintSymbol(const LVSymbol *Symbol) const { return true; } + bool doPrintType(const LVType *Type) const { return true; } + + static LVReader &getInstance(); + static void setInstance(LVReader *Reader); + + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +inline LVReader &getReader() { return LVReader::getInstance(); } +inline LVSplitContext &getReaderSplitContext() { + return getReader().getSplitContext(); +} +inline LVScopeCompileUnit *getReaderCompileUnit() { + return getReader().getCompileUnit(); +} + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_CORE_LVREADER_H Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVScope.h @@ -16,6 +16,7 @@ #include "llvm/DebugInfo/LogicalView/Core/LVElement.h" #include "llvm/DebugInfo/LogicalView/Core/LVSort.h" +#include #include namespace llvm { @@ -50,6 +51,536 @@ }; using LVScopeKindSet = std::set; +using LVOffsetElementMap = std::map; + +// Class to represent a DWARF Scope. +class LVScope : public LVElement { + enum class Property { + HasDiscriminator, + CanHaveRanges, + CanHaveLines, + HasGlobals, + HasLocals, + HasLines, + HasScopes, + HasSymbols, + HasTypes, + IsComdat, + HasComdatScopes, // Compile Unit has comdat functions. + HasRanges, + AddedMissing, // Added missing referenced symbols. + LastEntry + }; + + // Typed bitvector with kinds and properties for this scope. + LVProperties Kinds; + LVProperties Properties; + + // Decide if the scope will be printed, using some conditions given by: + // only-globals, only-locals, a-pattern. + bool resolvePrinting() const; + + // Traverse the scope parent tree, executing the given callback function + // on each scope. + void traverseParents(LVScopeGetFunction GetFunction, + LVScopeSetFunction SetFunction); + +protected: + // Types, Symbols, Scopes, Lines in this scope. + LVAutoTypes *Types = nullptr; + LVAutoSymbols *Symbols = nullptr; + LVAutoScopes *Scopes = nullptr; + LVAutoLines *Lines = nullptr; + + // Vector of elements (types, scopes and symbols). + // It is the union of (*Types, *Symbols and *Scopes) to be used for + // the following reasons: + // - Preserve the order the logical elements are read in. + // - To have a single container with all the logical elements, when + // the traversal does not require any specific element kind. + LVElements *Children = nullptr; + + // Resolve the template parameters/arguments relationship. + void resolveTemplate(); + void printEncodedArgs(raw_ostream &OS, bool Full) const; + + virtual void printSizes(raw_ostream &OS) const {} + virtual void printSummary(raw_ostream &OS) const {} + + // Encoded template arguments. + virtual StringRef getEncodedArgs() const { return StringRef(); } + virtual void setEncodedArgs(StringRef EncodedArgs) {} + +public: + LVScope() : LVElement(LVSubclassID::LV_SCOPE) { + setIsScope(); + setIncludeInPrint(); + } + LVScope(const LVScope &) = delete; + LVScope &operator=(const LVScope &) = delete; + virtual ~LVScope(); + + static bool classof(const LVElement *Element) { + return Element->getSubclassID() == LVSubclassID::LV_SCOPE; + } + + KIND(LVScopeKind, IsAggregate); + KIND(LVScopeKind, IsArray); + KIND_2(LVScopeKind, IsBlock, CanHaveRanges, CanHaveLines); + KIND_1(LVScopeKind, IsCallSite, IsFunction); + KIND_1(LVScopeKind, IsCatchBlock, IsBlock); + KIND_1(LVScopeKind, IsClass, IsAggregate); + KIND_3(LVScopeKind, IsCompileUnit, CanHaveRanges, CanHaveLines, + TransformName); + KIND_1(LVScopeKind, IsEntryPoint, IsFunction); + KIND(LVScopeKind, IsEnumeration); + KIND_2(LVScopeKind, IsFunction, CanHaveRanges, CanHaveLines); + KIND_1(LVScopeKind, IsFunctionType, IsFunction); + KIND_2(LVScopeKind, IsInlinedFunction, IsFunction, IsInlined); + KIND_1(LVScopeKind, IsLabel, IsFunction); + KIND_1(LVScopeKind, IsLexicalBlock, IsBlock); + KIND(LVScopeKind, IsMember); + KIND(LVScopeKind, IsNamespace); + KIND_1(LVScopeKind, IsRoot, TransformName); + KIND_1(LVScopeKind, IsStructure, IsAggregate); + KIND_1(LVScopeKind, IsSubprogram, IsFunction); + KIND(LVScopeKind, IsTemplate); + KIND(LVScopeKind, IsTemplateAlias); + KIND(LVScopeKind, IsTemplatePack); + KIND_1(LVScopeKind, IsTryBlock, IsBlock); + KIND_1(LVScopeKind, IsUnion, IsAggregate); + + PROPERTY(Property, HasDiscriminator); + PROPERTY(Property, CanHaveRanges); + PROPERTY(Property, CanHaveLines); + PROPERTY(Property, HasGlobals); + PROPERTY(Property, HasLocals); + PROPERTY(Property, HasLines); + PROPERTY(Property, HasScopes); + PROPERTY(Property, HasSymbols); + PROPERTY(Property, HasTypes); + PROPERTY(Property, IsComdat); + PROPERTY(Property, HasComdatScopes); + PROPERTY(Property, HasRanges); + PROPERTY(Property, AddedMissing); + + bool isCompileUnit() const override { return getIsCompileUnit(); } + bool isRoot() const override { return getIsRoot(); } + + const char *kind() const override; + + // Get the specific children. + const LVLines *getLines() const { return Lines; } + const LVScopes *getScopes() const { return Scopes; } + const LVSymbols *getSymbols() const { return Symbols; } + const LVTypes *getTypes() const { return Types; } + const LVElements *getChildren() const { return Children; } + + void addElement(LVElement *Element); + void addElement(LVLine *Line); + void addElement(LVScope *Scope); + void addElement(LVSymbol *Symbol); + void addElement(LVType *Type); + void addToChildren(LVElement *Element); + + // Add the missing elements from the given 'Reference', which is the + // scope associated with any DW_AT_specification, DW_AT_abstract_origin. + void addMissingElements(LVScope *Reference); + + // Traverse the scope parent tree and the children, executing the given + // callback function on each element. + void traverseParentsAndChildren(LVObjectGetFunction GetFunction, + LVObjectSetFunction SetFunction); + + // Get the size of specific children. + size_t lineCount() const { return Lines ? Lines->size() : 0; } + size_t scopeCount() const { return Scopes ? Scopes->size() : 0; } + size_t symbolCount() const { return Symbols ? Symbols->size() : 0; } + size_t typeCount() const { return Types ? Types->size() : 0; } + + Error doPrint(bool Split, bool Match, bool Print, raw_ostream &OS, + bool Full = true) const override; + // Sort the logical elements using the criteria specified by the + // command line option '--output-sort'. + void sort(); + + // Get template parameter types. + bool getTemplateParameterTypes(LVTypes &Params); + + // DW_AT_specification, DW_AT_abstract_origin, DW_AT_extension. + virtual LVScope *getReference() const { return nullptr; } + + // Follow a chain of references given by DW_AT_abstract_origin and/or + // DW_AT_specification and update the scope name. + StringRef resolveReferencesChain(); + + bool removeElement(LVElement *Element) override; + void updateLevel(LVScope *Parent, bool Moved) override; + + void resolve() override; + void resolveName() override; + void resolveReferences() override; + + // Return the chain of parents as a string. + void getQualifiedName(std::string &QualifiedName) const; + // Encode the template arguments. + void encodeTemplateArguments(std::string &Name) const; + void encodeTemplateArguments(std::string &Name, const LVTypes *Types) const; + + void resolveElements(); + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + virtual void printMatchedElements(raw_ostream &OS, bool UseMatchedElements) {} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +// Class to represent a DWARF Union/Structure/Class. +class LVScopeAggregate final : public LVScope { + LVScope *Reference = nullptr; // DW_AT_specification, DW_AT_abstract_origin. + size_t EncodedArgsIndex = 0; // Template encoded arguments. + +public: + LVScopeAggregate() : LVScope() {} + LVScopeAggregate(const LVScopeAggregate &) = delete; + LVScopeAggregate &operator=(const LVScopeAggregate &) = delete; + ~LVScopeAggregate() = default; + + // DW_AT_specification, DW_AT_abstract_origin. + LVScope *getReference() const override { return Reference; } + void setReference(LVScope *Scope) override { + Reference = Scope; + setHasReference(); + } + void setReference(LVElement *Element) override { + setReference(static_cast(Element)); + } + + StringRef getEncodedArgs() const override { + return getStringPool().getString(EncodedArgsIndex); + } + void setEncodedArgs(StringRef EncodedArgs) override { + EncodedArgsIndex = getStringPool().getIndex(EncodedArgs); + } + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF Template alias. +class LVScopeAlias final : public LVScope { +public: + LVScopeAlias() : LVScope() { + setIsTemplateAlias(); + setIsTemplate(); + } + LVScopeAlias(const LVScopeAlias &) = delete; + LVScopeAlias &operator=(const LVScopeAlias &) = delete; + ~LVScopeAlias() = default; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF array (DW_TAG_array_type). +class LVScopeArray final : public LVScope { +public: + LVScopeArray() : LVScope() { setIsArray(); } + LVScopeArray(const LVScopeArray &) = delete; + LVScopeArray &operator=(const LVScopeArray &) = delete; + ~LVScopeArray() = default; + + void resolveExtra() override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF Compilation Unit (CU). +class LVScopeCompileUnit final : public LVScope { + // Names (files and directories) used by the Compile Unit. + std::vector Filenames; + + // Toolchain producer. + size_t ProducerIndex = 0; + + // Compilation directory name. + size_t CompilationDirectoryIndex = 0; + + // Keep record of elements. They are needed at the compilation unit level + // to print the summary at the end of the printing. + LVCounter Allocated; + LVCounter Found; + LVCounter Printed; + + // Elements that match a given command line pattern. + LVElements MatchedElements; + LVScopes MatchedScopes; + + // It records the mapping between logical lines representing a debug line + // entry and its address in the text section. It is used to find a line + // giving its exact or closest address. + using LVAddressToLine = std::map; + LVAddressToLine AddressToLine; + + // Record scopes contribution in bytes to the debug information. + using LVSizesMap = std::map; + LVSizesMap Sizes; + LVOffset CUContributionSize = 0; + + // Record scope sizes indexed by lexical level. + // Setting an initial size that will cover a very deep nested scopes. + const size_t TotalInitialSize = 8; + using LVTotalsEntry = std::pair; + SmallVector Totals; + // Maximum seen lexical level. It is used to control how many entries + // in the 'Totals' vector are valid values. + LVLevel MaxSeenLevel = 0; + + void printScopeSize(const LVScope *Scope, raw_ostream &OS); + void printScopeSize(const LVScope *Scope, raw_ostream &OS) const { + (const_cast(this))->printScopeSize(Scope, OS); + } + void printTotals(raw_ostream &OS) const; + +protected: + void printSizes(raw_ostream &OS) const override; + void printSummary(raw_ostream &OS) const override; + +public: + LVScopeCompileUnit() : LVScope(), Totals(TotalInitialSize, {0, 0.0}) { + setIsCompileUnit(); + } + LVScopeCompileUnit(const LVScopeCompileUnit &) = delete; + LVScopeCompileUnit &operator=(const LVScopeCompileUnit &) = delete; + ~LVScopeCompileUnit() = default; + + // Get the line located at the given address. + LVLine *lineLowerBound(LVAddress Address) const; + LVLine *lineUpperBound(LVAddress Address) const; + + StringRef getCompilationDirectory() const { + return getStringPool().getString(CompilationDirectoryIndex); + } + void setCompilationDirectory(StringRef CompilationDirectory) { + CompilationDirectoryIndex = getStringPool().getIndex(CompilationDirectory); + } + + StringRef getFilename(size_t Index) const; + void addFilename(StringRef Name) { + Filenames.push_back(getStringPool().getIndex(Name)); + } + + StringRef getProducer() const override { + return getStringPool().getString(ProducerIndex); + } + void setProducer(StringRef ProducerName) override { + ProducerIndex = getStringPool().getIndex(ProducerName); + } + + void printLocalNames(raw_ostream &OS, bool Full = true) const; + void printSummary(raw_ostream &OS, const LVCounter &Counter, + const char *Header) const; + + void incrementPrintedLines(); + void incrementPrintedScopes(); + void incrementPrintedSymbols(); + void incrementPrintedTypes(); + + // Values are used by '--summary' option (allocated). + void increment(LVLine *Line); + void increment(LVScope *Scope); + void increment(LVSymbol *Symbol); + void increment(LVType *Type); + + // A new element has been added to the scopes tree. Take the following steps: + // Increase the added element counters, for printing summary. + // Notify the Reader if element comparison. + void addedElement(LVLine *Line); + void addedElement(LVScope *Scope); + void addedElement(LVSymbol *Symbol); + void addedElement(LVType *Type); + + void addSize(LVScope *Scope, LVOffset Lower, LVOffset Upper); + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + void printMatchedElements(raw_ostream &OS, bool UseMatchedElements) override; +}; + +// Class to represent a DWARF enumerator (DW_TAG_enumeration_type). +class LVScopeEnumeration final : public LVScope { +public: + LVScopeEnumeration() : LVScope() { setIsEnumeration(); } + LVScopeEnumeration(const LVScopeEnumeration &) = delete; + LVScopeEnumeration &operator=(const LVScopeEnumeration &) = delete; + ~LVScopeEnumeration() = default; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF formal parameter pack +// (DW_TAG_GNU_formal_parameter_pack). +class LVScopeFormalPack final : public LVScope { +public: + LVScopeFormalPack() : LVScope() { setIsTemplatePack(); } + LVScopeFormalPack(const LVScopeFormalPack &) = delete; + LVScopeFormalPack &operator=(const LVScopeFormalPack &) = delete; + ~LVScopeFormalPack() = default; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF Function. +class LVScopeFunction : public LVScope { + LVScope *Reference = nullptr; // DW_AT_specification, DW_AT_abstract_origin. + size_t LinkageNameIndex = 0; // Function DW_AT_linkage_name attribute. + size_t EncodedArgsIndex = 0; // Template encoded arguments. + +public: + LVScopeFunction() : LVScope() {} + LVScopeFunction(const LVScopeFunction &) = delete; + LVScopeFunction &operator=(const LVScopeFunction &) = delete; + virtual ~LVScopeFunction() = default; + + // DW_AT_specification, DW_AT_abstract_origin. + LVScope *getReference() const override { return Reference; } + void setReference(LVScope *Scope) override { + Reference = Scope; + setHasReference(); + } + void setReference(LVElement *Element) override { + setReference(static_cast(Element)); + } + + StringRef getEncodedArgs() const override { + return getStringPool().getString(EncodedArgsIndex); + } + void setEncodedArgs(StringRef EncodedArgs) override { + EncodedArgsIndex = getStringPool().getIndex(EncodedArgs); + } + + void setLinkageName(StringRef LinkageName) override { + LinkageNameIndex = getStringPool().getIndex(LinkageName); + } + StringRef getLinkageName() const override { + return getStringPool().getString(LinkageNameIndex); + } + size_t getLinkageNameIndex() const override { return LinkageNameIndex; } + + void setName(StringRef ObjectName) override; + + void resolveExtra() override; + void resolveReferences() override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF inlined function. +class LVScopeFunctionInlined final : public LVScopeFunction { + size_t CallFilenameIndex = 0; + uint32_t CallLineNumber = 0; + uint32_t Discriminator = 0; + +public: + LVScopeFunctionInlined() : LVScopeFunction() { setIsInlinedFunction(); } + LVScopeFunctionInlined(const LVScopeFunctionInlined &) = delete; + LVScopeFunctionInlined &operator=(const LVScopeFunctionInlined &) = delete; + ~LVScopeFunctionInlined() = default; + + uint32_t getDiscriminator() const override { return Discriminator; } + void setDiscriminator(uint32_t Value) override { + Discriminator = Value; + setHasDiscriminator(); + } + + uint32_t getCallLineNumber() const override { return CallLineNumber; } + void setCallLineNumber(uint32_t Number) override { CallLineNumber = Number; } + size_t getCallFilenameIndex() const override { return CallFilenameIndex; } + void setCallFilenameIndex(size_t Index) override { + CallFilenameIndex = Index; + } + + // Line number for display; in the case of Inlined Functions, we use the + // DW_AT_call_line attribute; otherwise use DW_AT_decl_line attribute. + std::string lineNumberAsString(bool ShowZero = false) const override { + return lineAsString(getCallLineNumber(), getDiscriminator(), ShowZero); + } + + void resolveExtra() override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF subroutine type. +class LVScopeFunctionType final : public LVScopeFunction { +public: + LVScopeFunctionType() : LVScopeFunction() { setIsFunctionType(); } + LVScopeFunctionType(const LVScopeFunctionType &) = delete; + LVScopeFunctionType &operator=(const LVScopeFunctionType &) = delete; + ~LVScopeFunctionType() = default; + + void resolveExtra() override; +}; + +// Class to represent a DWARF Namespace. +class LVScopeNamespace final : public LVScope { + LVScope *Reference = nullptr; // Reference to DW_AT_extension attribute. + +public: + LVScopeNamespace() : LVScope() { setIsNamespace(); } + LVScopeNamespace(const LVScopeNamespace &) = delete; + LVScopeNamespace &operator=(const LVScopeNamespace &) = delete; + ~LVScopeNamespace() = default; + + // Access DW_AT_extension reference. + LVScope *getReference() const override { return Reference; } + void setReference(LVScope *Scope) override { + Reference = Scope; + setHasReference(); + } + void setReference(LVElement *Element) override { + setReference(static_cast(Element)); + } + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent the binary file being analyzed. +class LVScopeRoot final : public LVScope { + size_t FileFormatNameIndex = 0; + +public: + LVScopeRoot() : LVScope() { setIsRoot(); } + LVScopeRoot(const LVScopeRoot &) = delete; + LVScopeRoot &operator=(const LVScopeRoot &) = delete; + ~LVScopeRoot() = default; + + StringRef getFileFormatName() const { + return getStringPool().getString(FileFormatNameIndex); + } + void setFileFormatName(StringRef FileFormatName) { + FileFormatNameIndex = getStringPool().getIndex(FileFormatName); + } + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + Error doPrintMatches(bool Split, raw_ostream &OS, + bool UseMatchedElements) const; +}; + +// Class to represent a DWARF template parameter pack +// (DW_TAG_GNU_template_parameter_pack). +class LVScopeTemplatePack final : public LVScope { +public: + LVScopeTemplatePack() : LVScope() { setIsTemplatePack(); } + LVScopeTemplatePack(const LVScopeTemplatePack &) = delete; + LVScopeTemplatePack &operator=(const LVScopeTemplatePack &) = delete; + ~LVScopeTemplatePack() = default; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + } // end namespace logicalview } // end namespace llvm Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVSort.h @@ -16,6 +16,8 @@ namespace llvm { namespace logicalview { +class LVObject; + // Object Sorting Mode. enum class LVSortMode { None = 0, // No given sort. @@ -25,6 +27,23 @@ Offset // Sort by offset. }; +// Type of function to be called when sorting an object. +using LVSortValue = int; +using LVSortFunction = LVSortValue (*)(const LVObject *LHS, + const LVObject *RHS); + +// Get the comparator function, based on the command line options. +LVSortFunction getSortFunction(); + +// Comparator functions that can be used for sorting. +LVSortValue compareKind(const LVObject *LHS, const LVObject *RHS); +LVSortValue compareLine(const LVObject *LHS, const LVObject *RHS); +LVSortValue compareName(const LVObject *LHS, const LVObject *RHS); +LVSortValue compareOffset(const LVObject *LHS, const LVObject *RHS); +LVSortValue sortByKind(const LVObject *LHS, const LVObject *RHS); +LVSortValue sortByLine(const LVObject *LHS, const LVObject *RHS); +LVSortValue sortByName(const LVObject *LHS, const LVObject *RHS); + } // end namespace logicalview } // end namespace llvm Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVSupport.h @@ -26,6 +26,89 @@ namespace llvm { namespace logicalview { +template +using TypeIsValid = std::bool_constant::value>; + +// Utility class to help memory management and perform an automatic cleaning. +template +class LVAutoSmallVector : public SmallVector { + static_assert(TypeIsValid::value, "T must be a pointer type"); + +public: + using iterator = typename SmallVector::iterator; + LVAutoSmallVector() : SmallVector::SmallVector() {} + + ~LVAutoSmallVector() { + // Destroy the constructed elements in the vector. + for (auto *Item : *this) + delete Item; + } +}; + +// Used to record specific characteristics about the objects. +template class LVProperties { + SmallBitVector Bits = SmallBitVector(static_cast(T::LastEntry) + 1); + +public: + LVProperties() = default; + + void set(T Idx) { Bits[static_cast(Idx)] = 1; } + void reset(T Idx) { Bits[static_cast(Idx)] = 0; } + bool get(T Idx) const { return Bits[static_cast(Idx)]; } +}; + +// Generate get, set and reset 'bool' functions for LVProperties instances. +// FAMILY: instance name. +// ENUM: enumeration instance. +// FIELD: enumerator instance. +// F1, F2, F3: optional 'set' functions to be called. +#define BOOL_BIT(FAMILY, ENUM, FIELD) \ + bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); } \ + void set##FIELD() { FAMILY.set(ENUM::FIELD); } \ + void reset##FIELD() { FAMILY.reset(ENUM::FIELD); } + +#define BOOL_BIT_1(FAMILY, ENUM, FIELD, F1) \ + bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); } \ + void set##FIELD() { \ + FAMILY.set(ENUM::FIELD); \ + set##F1(); \ + } \ + void reset##FIELD() { FAMILY.reset(ENUM::FIELD); } + +#define BOOL_BIT_2(FAMILY, ENUM, FIELD, F1, F2) \ + bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); } \ + void set##FIELD() { \ + FAMILY.set(ENUM::FIELD); \ + set##F1(); \ + set##F2(); \ + } \ + void reset##FIELD() { FAMILY.reset(ENUM::FIELD); } + +#define BOOL_BIT_3(FAMILY, ENUM, FIELD, F1, F2, F3) \ + bool get##FIELD() const { return FAMILY.get(ENUM::FIELD); } \ + void set##FIELD() { \ + FAMILY.set(ENUM::FIELD); \ + set##F1(); \ + set##F2(); \ + set##F3(); \ + } \ + void reset##FIELD() { FAMILY.reset(ENUM::FIELD); } + +// Generate get, set and reset functions for 'properties'. +#define PROPERTY(ENUM, FIELD) BOOL_BIT(Properties, ENUM, FIELD) +#define PROPERTY_1(ENUM, FIELD, F1) BOOL_BIT_1(Properties, ENUM, FIELD, F1) +#define PROPERTY_2(ENUM, FIELD, F1, F2) \ + BOOL_BIT_2(Properties, ENUM, FIELD, F1, F2) +#define PROPERTY_3(ENUM, FIELD, F1, F2, F3) \ + BOOL_BIT_3(Properties, ENUM, FIELD, F1, F2, F3) + +// Generate get, set and reset functions for 'kinds'. +#define KIND(ENUM, FIELD) BOOL_BIT(Kinds, ENUM, FIELD) +#define KIND_1(ENUM, FIELD, F1) BOOL_BIT_1(Kinds, ENUM, FIELD, F1) +#define KIND_2(ENUM, FIELD, F1, F2) BOOL_BIT_2(Kinds, ENUM, FIELD, F1, F2) +#define KIND_3(ENUM, FIELD, F1, F2, F3) \ + BOOL_BIT_3(Kinds, ENUM, FIELD, F1, F2, F3) + const int HEX_WIDTH = 12; inline FormattedNumber hexValue(uint64_t N, unsigned Width = HEX_WIDTH, bool Upper = false) { @@ -45,6 +128,36 @@ return (Twine("[") + Twine(hexString(Value)) + Twine("]")).str(); } +// Return a string with the First and Others separated by spaces. +template +std::string formatAttributes(const StringRef First, Args... Others) { + const auto List = {First, Others...}; + std::stringstream Stream; + size_t Size = 0; + for (const StringRef &Item : List) { + Stream << (Size ? " " : "") << Item.str(); + Size = Item.size(); + } + Stream << (Size ? " " : ""); + return Stream.str(); +} + +// Unified and flattened pathnames. +std::string transformPath(StringRef Path); +std::string flattenedFilePath(StringRef Path); + +inline std::string formattedKind(StringRef Kind) { + return (Twine("{") + Twine(Kind) + Twine("}")).str(); +} + +inline std::string formattedName(StringRef Name) { + return (Twine("'") + Twine(Name) + Twine("'")).str(); +} + +inline std::string formattedNames(StringRef Name1, StringRef Name2) { + return (Twine("'") + Twine(Name1) + Twine(Name2) + Twine("'")).str(); +} + } // end namespace logicalview } // end namespace llvm Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVSymbol.h @@ -31,6 +31,97 @@ }; using LVSymbolKindSet = std::set; +class LVSymbol final : public LVElement { + enum class Property { HasLocation, FillGaps, LastEntry }; + + // Typed bitvector with kinds and properties for this symbol. + LVProperties Kinds; + LVProperties Properties; + + // CodeView symbol Linkage name. + size_t LinkageNameIndex = 0; + + // Reference to DW_AT_specification, DW_AT_abstract_origin attribute. + LVSymbol *Reference = nullptr; + + // Bitfields length. + uint32_t BitSize = 0; + + // Index in the String pool representing any initial value. + size_t ValueIndex = 0; + +public: + LVSymbol() : LVElement(LVSubclassID::LV_SYMBOL) { + setIsSymbol(); + setIncludeInPrint(); + } + LVSymbol(const LVSymbol &) = delete; + LVSymbol &operator=(const LVSymbol &) = delete; + ~LVSymbol() = default; + + static bool classof(const LVElement *Element) { + return Element->getSubclassID() == LVSubclassID::LV_SYMBOL; + } + + KIND(LVSymbolKind, IsCallSiteParameter); + KIND(LVSymbolKind, IsConstant); + KIND(LVSymbolKind, IsInheritance); + KIND(LVSymbolKind, IsMember); + KIND(LVSymbolKind, IsParameter); + KIND(LVSymbolKind, IsUnspecified); + KIND(LVSymbolKind, IsVariable); + + PROPERTY(Property, HasLocation); + PROPERTY(Property, FillGaps); + + const char *kind() const override; + + // Access DW_AT_specification, DW_AT_abstract_origin reference. + LVSymbol *getReference() const { return Reference; } + void setReference(LVSymbol *Symbol) override { + Reference = Symbol; + setHasReference(); + } + void setReference(LVElement *Element) override { + assert((!Element || isa(Element)) && "Invalid element"); + setReference(static_cast(Element)); + } + + void setLinkageName(StringRef LinkageName) override { + LinkageNameIndex = getStringPool().getIndex(LinkageName); + } + StringRef getLinkageName() const override { + return getStringPool().getString(LinkageNameIndex); + } + size_t getLinkageNameIndex() const override { return LinkageNameIndex; } + + uint32_t getBitSize() const override { return BitSize; } + void setBitSize(uint32_t Size) override { BitSize = Size; } + + // Process the values for a DW_AT_const_value. + std::string getValue() const override { + return std::string(getStringPool().getString(ValueIndex)); + } + void setValue(StringRef Value) override { + ValueIndex = getStringPool().getIndex(Value); + } + size_t getValueIndex() const override { return ValueIndex; } + + // Follow a chain of references given by DW_AT_abstract_origin and/or + // DW_AT_specification and update the symbol name. + StringRef resolveReferencesChain(); + + void resolveName() override; + void resolveReferences() override; + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + } // end namespace logicalview } // end namespace llvm Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVType.h @@ -45,6 +45,200 @@ }; using LVTypeKindSelection = std::set; +// Class to represent a DWARF Type. +class LVType : public LVElement { + enum class Property { IsSubrangeCount, LastEntry }; + + // Typed bitvector with kinds and properties for this type. + LVProperties Kinds; + LVProperties Properties; + +public: + LVType() : LVElement(LVSubclassID::LV_TYPE) { setIsType(); } + LVType(const LVType &) = delete; + LVType &operator=(const LVType &) = delete; + virtual ~LVType() = default; + + static bool classof(const LVElement *Element) { + return Element->getSubclassID() == LVSubclassID::LV_TYPE; + } + + KIND(LVTypeKind, IsBase); + KIND(LVTypeKind, IsConst); + KIND(LVTypeKind, IsEnumerator); + KIND(LVTypeKind, IsImport); + KIND_1(LVTypeKind, IsImportDeclaration, IsImport); + KIND_1(LVTypeKind, IsImportModule, IsImport); + KIND(LVTypeKind, IsPointer); + KIND(LVTypeKind, IsPointerMember); + KIND(LVTypeKind, IsReference); + KIND(LVTypeKind, IsRestrict); + KIND(LVTypeKind, IsRvalueReference); + KIND(LVTypeKind, IsSubrange); + KIND(LVTypeKind, IsTemplateParam); + KIND_1(LVTypeKind, IsTemplateTemplateParam, IsTemplateParam); + KIND_1(LVTypeKind, IsTemplateTypeParam, IsTemplateParam); + KIND_1(LVTypeKind, IsTemplateValueParam, IsTemplateParam); + KIND(LVTypeKind, IsTypedef); + KIND(LVTypeKind, IsUnaligned); + KIND(LVTypeKind, IsUnspecified); + KIND(LVTypeKind, IsVolatile); + KIND(LVTypeKind, IsModifier); + + PROPERTY(Property, IsSubrangeCount); + + const char *kind() const override; + + // Follow a chain of references given by DW_AT_abstract_origin and/or + // DW_AT_specification and update the type name. + StringRef resolveReferencesChain(); + + bool isBase() const override { return getIsBase(); } + bool isTemplateParam() const override { return getIsTemplateParam(); } + + // Encode the specific template argument. + virtual void encodeTemplateArgument(std::string &Name) const {} + + // Return the underlying type for a type definition. + virtual LVElement *getUnderlyingType() { return nullptr; } + virtual void setUnderlyingType(LVElement *Element) {} + + void resolveName() override; + void resolveReferences() override; + + void print(raw_ostream &OS, bool Full = true) const override; + void printExtra(raw_ostream &OS, bool Full = true) const override; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const override { print(dbgs()); } +#endif +}; + +// Class to represent DW_TAG_typedef_type. +class LVTypeDefinition final : public LVType { +public: + LVTypeDefinition() : LVType() { + setIsTypedef(); + setIncludeInPrint(); + } + LVTypeDefinition(const LVTypeDefinition &) = delete; + LVTypeDefinition &operator=(const LVTypeDefinition &) = delete; + ~LVTypeDefinition() = default; + + // Return the underlying type for a type definition. + LVElement *getUnderlyingType() override; + void setUnderlyingType(LVElement *Element) override { setType(Element); } + + void resolveExtra() override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DW_TAG_enumerator. +class LVTypeEnumerator final : public LVType { + // Index in the String pool representing any initial value. + size_t ValueIndex = 0; + +public: + LVTypeEnumerator() : LVType() { + setIsEnumerator(); + setIncludeInPrint(); + } + LVTypeEnumerator(const LVTypeEnumerator &) = delete; + LVTypeEnumerator &operator=(const LVTypeEnumerator &) = delete; + ~LVTypeEnumerator() = default; + + // Process the values for a DW_TAG_enumerator. + std::string getValue() const override { + return std::string(getStringPool().getString(ValueIndex)); + } + void setValue(StringRef Value) override { + ValueIndex = getStringPool().getIndex(Value); + } + size_t getValueIndex() const override { return ValueIndex; } + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent DW_TAG_imported_module / DW_TAG_imported_declaration. +class LVTypeImport final : public LVType { +public: + LVTypeImport() : LVType() { setIncludeInPrint(); } + LVTypeImport(const LVTypeImport &) = delete; + LVTypeImport &operator=(const LVTypeImport &) = delete; + ~LVTypeImport() = default; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DWARF Template parameter holder (type or param). +class LVTypeParam final : public LVType { + // Index in the String pool representing any initial value. + size_t ValueIndex = 0; + +public: + LVTypeParam(); + LVTypeParam(const LVTypeParam &) = delete; + LVTypeParam &operator=(const LVTypeParam &) = delete; + ~LVTypeParam() = default; + + // Template parameter value. + std::string getValue() const override { + return std::string(getStringPool().getString(ValueIndex)); + } + void setValue(StringRef Value) override { + ValueIndex = getStringPool().getIndex(Value); + } + size_t getValueIndex() const override { return ValueIndex; } + + // Encode the specific template argument. + void encodeTemplateArgument(std::string &Name) const override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + +// Class to represent a DW_TAG_subrange_type. +class LVTypeSubrange final : public LVType { + // Values describing the subrange bounds. + int64_t LowerBound = 0; // DW_AT_lower_bound or DW_AT_count value. + int64_t UpperBound = 0; // DW_AT_upper_bound value. + +public: + LVTypeSubrange() : LVType() { + setIsSubrange(); + setIncludeInPrint(); + } + LVTypeSubrange(const LVTypeSubrange &) = delete; + LVTypeSubrange &operator=(const LVTypeSubrange &) = delete; + ~LVTypeSubrange() = default; + + int64_t getCount() const override { + return getIsSubrangeCount() ? LowerBound : 0; + } + void setCount(int64_t Value) override { + LowerBound = Value; + setIsSubrangeCount(); + } + + int64_t getLowerBound() const override { return LowerBound; } + void setLowerBound(int64_t Value) override { LowerBound = Value; } + + int64_t getUpperBound() const override { return UpperBound; } + void setUpperBound(int64_t Value) override { UpperBound = Value; } + + std::pair getBounds() const override { + return {LowerBound, UpperBound}; + } + void setBounds(unsigned Lower, unsigned Upper) override { + LowerBound = Lower; + UpperBound = Upper; + } + + void resolveExtra() override; + + void printExtra(raw_ostream &OS, bool Full = true) const override; +}; + } // end namespace logicalview } // end namespace llvm Index: llvm/lib/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/LogicalView/CMakeLists.txt +++ llvm/lib/DebugInfo/LogicalView/CMakeLists.txt @@ -15,7 +15,16 @@ endmacro() add_lv_impl_folder(Core + Core/LVElement.cpp + Core/LVLine.cpp + Core/LVObject.cpp Core/LVOptions.cpp + Core/LVReader.cpp + Core/LVScope.cpp + Core/LVSort.cpp + Core/LVSupport.cpp + Core/LVSymbol.cpp + Core/LVType.cpp ) list(APPEND LIBLV_ADDITIONAL_HEADER_DIRS Index: llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp @@ -0,0 +1,464 @@ +//===-- 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" + +LVType *LVElement::getTypeAsType() const { + return ElementType && ElementType->getIsType() + ? static_cast(ElementType) + : nullptr; +} + +LVScope *LVElement::getTypeAsScope() const { + return ElementType && ElementType->getIsScope() + ? static_cast(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.c_str()); +} + +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"; + }); +} + +// 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); + } +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVLine.cpp @@ -0,0 +1,126 @@ +//===-- 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/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; +} + +// String used as padding for printing elements with no line number. +std::string LVLine::noLineAsString(bool ShowZero) const { + return (ShowZero || options().getAttributeZero()) ? (" 0 ") + : (" - "); +} + +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; +} + +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. +//===----------------------------------------------------------------------===// +void LVLineAssembler::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()); + OS << " " << formattedName(getName()); + OS << "\n"; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVObject.cpp @@ -0,0 +1,143 @@ +//===-- 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 + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Object" + +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); + + 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); +} + +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 { + 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(); +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -0,0 +1,177 @@ +//===-- 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/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include + +using namespace llvm; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "Reader" + +//===----------------------------------------------------------------------===// +// 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(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); + + // Delegate the scope tree creation to the specific reader. + if (Error Err = createScopes()) + return Err; + + // 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); + + return printScopes(); +} + +Error LVReader::printScopes() { + if (bool DoPrint = options().getPrintExecute()) { + if (Error Err = createSplitFolder()) + return Err; + + // Start printing from the root. + bool DoMatch = false; + 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"); +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -0,0 +1,1334 @@ +//===-- 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/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVOptions.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 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; +} + +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(Element)); + else if (Element->getIsScope()) + addElement(static_cast(Element)); + else if (Element->getIsSymbol()) + addElement(static_cast(Element)); + else if (Element->getIsLine()) + addElement(static_cast(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); +} + +// 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); +} + +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(); +} + +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(Scope); + getReader().setCompileUnit(CompileUnit); + CompileUnit->resolve(); + } +} + +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.c_str()); + } + } +} + +// 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,std::allocator> + // ... + // } + // 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 { + 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(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(&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; + } + } + } + + // 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(); + } + } + + return Error::success(); +} + +void LVScope::sort() { + // Preserve the lines order as they are associated with user code. + LVSortFunction SortFunction = getSortFunction(); + if (SortFunction) { + std::function 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->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 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); +} + +void LVScope::printEncodedArgs(raw_ostream &OS, bool Full) const { + if (options().getPrintFormatting() && options().getAttributeEncoded()) + printAttributes(OS, Full, "{Encoded} ", const_cast(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. + if (!(getIsRoot())) + 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"; +} + +//===----------------------------------------------------------------------===// +// DWARF Union/Structure/Class. +//===----------------------------------------------------------------------===// +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(this)); + } +} + +//===----------------------------------------------------------------------===// +// DWARF Template alias. +//===----------------------------------------------------------------------===// +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()); +} + +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%08x], Scope [0x%08x], Range [0x%08x:0x%08x], Size = %d\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; +} + +LVLine *LVScopeCompileUnit::lineLowerBound(LVAddress Address) const { + LVAddressToLine::const_iterator Iter = AddressToLine.lower_bound(Address); + return (Iter != AddressToLine.end()) ? Iter->second : nullptr; +} + +LVLine *LVScopeCompileUnit::lineUpperBound(LVAddress Address) const { + LVAddressToLine::const_iterator Iter = AddressToLine.upper_bound(Address); + if (Iter != AddressToLine.begin()) + Iter = std::prev(Iter); + return (Iter != AddressToLine.end()) ? Iter->second : nullptr; +} + +StringRef LVScopeCompileUnit::getFilename(size_t Index) const { + if (Index <= 0 || Index > Filenames.size()) + return StringRef(); + return getStringPool().getString(Filenames[Index - 1]); +} + +void LVScopeCompileUnit::incrementPrintedLines() { ++Printed.Lines; } +void LVScopeCompileUnit::incrementPrintedScopes() { ++Printed.Scopes; } +void LVScopeCompileUnit::incrementPrintedSymbols() { ++Printed.Symbols; } +void LVScopeCompileUnit::incrementPrintedTypes() { ++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. +void LVScopeCompileUnit::addedElement(LVLine *Line) { increment(Line); } +void LVScopeCompileUnit::addedElement(LVScope *Scope) { increment(Scope); } +void LVScopeCompileUnit::addedElement(LVSymbol *Symbol) { increment(Symbol); } +void LVScopeCompileUnit::addedElement(LVType *Type) { increment(Type); } + +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 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); +} + +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("%10d (%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 PrintScope = + [&](const LVScope *Scope) { + 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(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, 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(Element), OS); + + printTotals(OS); + } +} + +void LVScopeCompileUnit::print(raw_ostream &OS, bool Full) const { + // Reset counters for printed and found elements. + const_cast(this)->Found.reset(); + const_cast(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(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. + if (Full) { + printLocalNames(OS, Full); + } +} + +//===----------------------------------------------------------------------===// +// DWARF enumeration (DW_TAG_enumeration_type). +//===----------------------------------------------------------------------===// +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). +//===----------------------------------------------------------------------===// +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(); +} + +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); + if (Reference) + Reference->printReference(OS, Full, const_cast(this)); + } +} + +//===----------------------------------------------------------------------===// +// DWARF inlined function (DW_TAG_inlined_function). +//===----------------------------------------------------------------------===// +void LVScopeFunctionInlined::resolveExtra() { + // Check if we need to encode the template arguments. + if (getIsTemplate()) + resolveTemplate(); +} + +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.c_str()); +} + +//===----------------------------------------------------------------------===// +// DWARF namespace (DW_TAG_namespace). +//===----------------------------------------------------------------------===// +void LVScopeNamespace::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; + + if (Full) { + if (LVScope *Reference = getReference()) + Reference->printReference(OS, Full, const_cast(this)); + } +} + +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(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(&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). +//===----------------------------------------------------------------------===// +void LVScopeTemplatePack::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " " << formattedName(getName()) << "\n"; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVSort.cpp @@ -0,0 +1,99 @@ +//===-- 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 + +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 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 Left( + LHS->kind(), LHS->getName(), LHS->getLineNumber(), LHS->getOffset()); + std::tuple 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 Left( + LHS->getLineNumber(), LHS->getName(), LHS->kind(), LHS->getOffset()); + std::tuple 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 Left( + LHS->getName(), LHS->getLineNumber(), LHS->kind(), LHS->getOffset()); + std::tuple Right( + RHS->getName(), RHS->getLineNumber(), RHS->kind(), RHS->getOffset()); + return Left < Right; +} + +LVSortFunction llvm::logicalview::getSortFunction() { + using LVSortInfo = std::map; + 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; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp =================================================================== --- /dev/null +++ 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 + +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; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVSymbol.cpp @@ -0,0 +1,164 @@ +//===-- 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/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; +} + +void LVSymbol::resolveName() { + if (getIsResolvedName()) + return; + setIsResolvedName(); + + LVElement::resolveName(); +} + +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::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(this)); + if (LVSymbol *Reference = getReference()) + Reference->printReference(OS, Full, const_cast(this)); + } +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp @@ -0,0 +1,346 @@ +//===-- 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/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; +} + +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' + // {Inherits} -> 'ArrayBase' + // {TemplateType} 'taTYPE' -> 'AtomTable' + // {Member} 'mData' -> '* taTYPE' + // + // b) Template Parameters are resolved: + // {Class} 'ConstArray' + // {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(); +} + +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::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()); +} + +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). +//===----------------------------------------------------------------------===// +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). +//===----------------------------------------------------------------------===// +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 a_float; + // std::type a_int; + // We must generate names like: + // "std::type,std::allocator,false>" + // "std::type,std::allocator,false>" + // Instead of the incomplete names: + // "type" + // "type" + + 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()); +} + +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); +} + +void LVTypeSubrange::printExtra(raw_ostream &OS, bool Full) const { + OS << formattedKind(kind()) << " -> " << typeOffsetAsString() + << formattedName(getTypeName()) << " " << formattedName(getName()) << "\n"; +} Index: llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt +++ llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt @@ -4,6 +4,7 @@ add_llvm_unittest(DebugInfoLogicalViewTests CommandLineOptionsTest.cpp + LogicalElementsTest.cpp StringPoolTest.cpp ) Index: llvm/unittests/DebugInfo/LogicalView/LogicalElementsTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/LogicalElementsTest.cpp @@ -0,0 +1,342 @@ +//===- llvm/unittest/DebugInfo/LogicalView/LogicalElementsTest.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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Core/LVLine.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" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +namespace { + +class ReaderTestElements : public LVReader { + // Types. + LVType *IntegerType = nullptr; + LVType *UnsignedType = nullptr; + LVType *GlobalType = nullptr; + LVType *LocalType = nullptr; + LVType *NestedType = nullptr; + LVTypeDefinition *TypeDefinitionOne = nullptr; + LVTypeDefinition *TypeDefinitionTwo = nullptr; + LVTypeEnumerator *EnumeratorOne = nullptr; + LVTypeEnumerator *EnumeratorTwo = nullptr; + LVTypeImport *TypeImport = nullptr; + LVTypeParam *TypeParam = nullptr; + LVTypeSubrange *TypeSubrange = nullptr; + + // Scopes. + LVScope *NestedScope = nullptr; + LVScopeAggregate *Aggregate = nullptr; + LVScopeArray *Array = nullptr; + LVScopeEnumeration *Enumeration = nullptr; + LVScopeFunction *Function = nullptr; + LVScopeFunction *ClassFunction = nullptr; + LVScopeFunctionInlined *InlinedFunction = nullptr; + LVScopeNamespace *Namespace = nullptr; + + // Symbols. + LVSymbol *GlobalVariable = nullptr; + LVSymbol *LocalVariable = nullptr; + LVSymbol *ClassMember = nullptr; + LVSymbol *NestedVariable = nullptr; + LVSymbol *Parameter = nullptr; + + // Lines. + LVLine *LocalLine = nullptr; + LVLine *NestedLine = nullptr; + +protected: + void add(LVScope *Parent, LVElement *Element); + template T *create() { + T *Element = new (std::nothrow) T(); + EXPECT_NE(Element, nullptr); + return Element; + } + void set(LVElement *Element, StringRef Name, LVOffset Offset, + uint32_t LineNumber = 0, LVElement *Type = nullptr); + +public: + ReaderTestElements(ScopedPrinter &W) : LVReader("", "", W) { + setInstance(this); + } + + Error createScopes() { return LVReader::createScopes(); } + Error printScopes() { return LVReader::printScopes(); } + + void createElements(); + void addElements(); + void initElements(); +}; + +// Helper function to add a logical element to a given scope. +void ReaderTestElements::add(LVScope *Parent, LVElement *Child) { + Parent->addElement(Child); + EXPECT_EQ(Child->getParent(), Parent); + EXPECT_EQ(Child->getLevel(), Parent->getLevel() + 1); +} + +// Helper function to set the initial values for a given logical element. +void ReaderTestElements::set(LVElement *Element, StringRef Name, + LVOffset Offset, uint32_t LineNumber, + LVElement *Type) { + Element->setName(Name); + Element->setOffset(Offset); + Element->setLineNumber(LineNumber); + Element->setType(Type); + EXPECT_EQ(Element->getName(), Name); + EXPECT_EQ(Element->getOffset(), Offset); + EXPECT_EQ(Element->getLineNumber(), LineNumber); + EXPECT_EQ(Element->getType(), Type); +} + +// Create the logical elements. +void ReaderTestElements::createElements() { + // Create scope root. + Error Err = createScopes(); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + Root = getScopesRoot(); + ASSERT_NE(Root, nullptr); + + // Create the logical types. + IntegerType = create(); + UnsignedType = create(); + GlobalType = create(); + LocalType = create(); + NestedType = create(); + EnumeratorOne = create(); + EnumeratorTwo = create(); + TypeDefinitionOne = create(); + TypeDefinitionTwo = create(); + TypeSubrange = create(); + TypeParam = create(); + TypeImport = create(); + + // Create the logical scopes. + NestedScope = create(); + Aggregate = create(); + Array = create(); + CompileUnit = create(); + Enumeration = create(); + Function = create(); + ClassFunction = create(); + InlinedFunction = create(); + Namespace = create(); + + // Create the logical symbols. + GlobalVariable = create(); + LocalVariable = create(); + ClassMember = create(); + NestedVariable = create(); + Parameter = create(); + + // Create the logical lines. + LocalLine = create(); + NestedLine = create(); +} + +// Create the logical view adding the created logical elements. +void ReaderTestElements::addElements() { + setCompileUnit(CompileUnit); + + // Root + // CompileUnit + // IntegerType + // UnsignedType + // Array + // TypeSubrange + // Function + // Parameter + // LocalVariable + // LocalType + // LocalLine + // InlinedFunction + // TypeImport + // TypeParam + // NestedScope + // NestedVariable + // NestedType + // NestedLine + // GlobalVariable + // GlobalType + // Namespace + // Aggregate + // ClassMember + // ClassFunction + // Enumeration + // EnumeratorOne + // EnumeratorTwo + // TypeDefinitionOne + // TypeDefinitionTwo + + add(Root, CompileUnit); + EXPECT_EQ(Root->lineCount(), 0u); + EXPECT_EQ(Root->scopeCount(), 1u); + EXPECT_EQ(Root->symbolCount(), 0u); + EXPECT_EQ(Root->typeCount(), 0u); + + // Add elements to CompileUnit. + add(CompileUnit, IntegerType); + add(CompileUnit, UnsignedType); + add(CompileUnit, Array); + add(CompileUnit, Function); + add(CompileUnit, GlobalVariable); + add(CompileUnit, GlobalType); + add(CompileUnit, Namespace); + EXPECT_EQ(CompileUnit->lineCount(), 0u); + EXPECT_EQ(CompileUnit->scopeCount(), 3u); + EXPECT_EQ(CompileUnit->symbolCount(), 1u); + EXPECT_EQ(CompileUnit->typeCount(), 3u); + + // Add elements to Namespace. + add(Namespace, Aggregate); + add(Namespace, Enumeration); + add(Namespace, TypeDefinitionOne); + add(Namespace, TypeDefinitionTwo); + EXPECT_EQ(Namespace->lineCount(), 0u); + EXPECT_EQ(Namespace->scopeCount(), 2u); + EXPECT_EQ(Namespace->symbolCount(), 0u); + EXPECT_EQ(Namespace->typeCount(), 2u); + + // Add elements to Function. + add(Function, Parameter); + add(Function, LocalVariable); + add(Function, LocalType); + add(Function, LocalLine); + add(Function, InlinedFunction); + add(Function, TypeImport); + add(Function, TypeParam); + add(Function, NestedScope); + EXPECT_EQ(Function->lineCount(), 1u); + EXPECT_EQ(Function->scopeCount(), 2u); + EXPECT_EQ(Function->symbolCount(), 2u); + EXPECT_EQ(Function->typeCount(), 3u); + + // Add elements to NestedScope. + add(NestedScope, NestedVariable); + add(NestedScope, NestedType); + add(NestedScope, NestedLine); + EXPECT_EQ(NestedScope->lineCount(), 1u); + EXPECT_EQ(NestedScope->scopeCount(), 0u); + EXPECT_EQ(NestedScope->symbolCount(), 1u); + EXPECT_EQ(NestedScope->typeCount(), 1u); + + // Add elements to Enumeration. + add(Enumeration, EnumeratorOne); + add(Enumeration, EnumeratorTwo); + EXPECT_EQ(Enumeration->lineCount(), 0u); + EXPECT_EQ(Enumeration->scopeCount(), 0u); + EXPECT_EQ(Enumeration->symbolCount(), 0u); + EXPECT_EQ(Enumeration->typeCount(), 2u); + + // Add elements to Aggregate. + add(Aggregate, ClassMember); + add(Aggregate, ClassFunction); + EXPECT_EQ(Aggregate->lineCount(), 0u); + EXPECT_EQ(Aggregate->scopeCount(), 1u); + EXPECT_EQ(Aggregate->symbolCount(), 1u); + EXPECT_EQ(Aggregate->typeCount(), 0u); + + // Add elements to Array. + add(Array, TypeSubrange); + EXPECT_EQ(Array->lineCount(), 0u); + EXPECT_EQ(Array->scopeCount(), 0u); + EXPECT_EQ(Array->symbolCount(), 0u); + EXPECT_EQ(Array->typeCount(), 1u); +} + +// Set initial values to logical elements. +void ReaderTestElements::initElements() { + setFilename("LogicalElements.obj"); + EXPECT_EQ(getFilename(), "LogicalElements.obj"); + + Root->setFileFormatName("FileFormat"); + EXPECT_EQ(Root->getFileFormatName(), "FileFormat"); + + // Types. + set(IntegerType, "int", 0x1000); + set(UnsignedType, "unsigned", 0x1010); + set(GlobalType, "GlobalType", 0x1020, 1020); + set(LocalType, "LocalType", 0x1030, 1030); + set(NestedType, "NestedType", 0x1040, 1040); + + set(TypeDefinitionOne, "INTEGER", 0x1040, 1040, IntegerType); + set(TypeDefinitionTwo, "INT", 0x1050, 1050, TypeDefinitionOne); + EXPECT_EQ(TypeDefinitionOne->getUnderlyingType(), IntegerType); + EXPECT_EQ(TypeDefinitionTwo->getUnderlyingType(), IntegerType); + + set(EnumeratorOne, "one", 0x1060, 1060); + EnumeratorOne->setValue("blue"); + EXPECT_EQ(EnumeratorOne->getValue(), "blue"); + + set(EnumeratorTwo, "two", 0x1070, 1070); + EnumeratorTwo->setValue("red"); + EXPECT_EQ(EnumeratorTwo->getValue(), "red"); + + set(TypeSubrange, "", 0x1080, 1080, IntegerType); + TypeSubrange->setCount(5); + EXPECT_EQ(TypeSubrange->getCount(), 5); + + TypeSubrange->setLowerBound(10); + TypeSubrange->setUpperBound(15); + EXPECT_EQ(TypeSubrange->getLowerBound(), 10); + EXPECT_EQ(TypeSubrange->getUpperBound(), 15); + + TypeSubrange->setBounds(20, 25); + std::pair Pair; + Pair = TypeSubrange->getBounds(); + EXPECT_EQ(Pair.first, 20u); + EXPECT_EQ(Pair.second, 25u); + + set(TypeParam, "INTEGER", 0x1090, 1090, UnsignedType); + TypeParam->setValue("10"); + EXPECT_EQ(TypeParam->getValue(), "10"); + + set(TypeImport, "", 0x1090, 1090, Aggregate); + EXPECT_EQ(TypeImport->getType(), Aggregate); + + // Scopes. + set(Aggregate, "Class", 0x2000, 2000); + set(Enumeration, "Colors", 0x2010, 2010); + set(Function, "function", 0x2020, 2020, GlobalType); + set(ClassFunction, "foo", 0x2030, 2030, TypeDefinitionTwo); + set(Namespace, "nsp", 0x2040, 2040); + set(NestedScope, "", 0x2050, 2050); + set(Array, "", 0x2060, 2060, UnsignedType); + set(InlinedFunction, "bar", 0x2070, 2070, TypeDefinitionOne); + set(CompileUnit, "test.cpp", 0x2080, 2080); + + // Symbols. + set(GlobalVariable, "GlobalVariable", 0x3000, 3000); + set(LocalVariable, "LocalVariable", 0x3010, 3010, TypeDefinitionOne); + set(ClassMember, "Member", 0x3020, 3020, IntegerType); + set(Parameter, "Param", 0x3030, 3030, UnsignedType); + set(NestedVariable, "NestedVariable", 0x3040, 3040); + + // Lines. + set(LocalLine, "", 0x4000, 4000); + set(NestedLine, "", 0x4010, 4010); +} + +TEST(LogicalViewTest, LogicalElements) { + ScopedPrinter W(outs()); + ReaderTestElements Reader(W); + + Reader.createElements(); + Reader.addElements(); + Reader.initElements(); +} + +} // namespace