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 @@ -206,6 +206,9 @@ size_t getNameIndex() const { return NameIndex; } size_t getQualifiedNameIndex() const { return QualifiedNameIndex; } + void setInnerComponent() { setInnerComponent(getName()); } + void setInnerComponent(StringRef Name); + // Element type name. StringRef getTypeName() const; @@ -264,6 +267,12 @@ StringRef accessibilityString(uint32_t Access = dwarf::DW_ACCESS_private) const; + // CodeView Accessibility Codes. + uint32_t getAccessibilityCode(codeview::MemberAccess Access); + void setAccessibilityCode(codeview::MemberAccess Access) { + AccessibilityCode = getAccessibilityCode(Access); + } + // DWARF Inline Codes. uint32_t getInlineCode() const { return InlineCode; } void setInlineCode(uint32_t Code) { InlineCode = Code; } @@ -275,6 +284,12 @@ StringRef virtualityString(uint32_t Virtuality = dwarf::DW_VIRTUALITY_none) const; + // CodeView Virtuality Codes. + uint32_t getVirtualityCode(codeview::MethodKind Virtuality); + void setVirtualityCode(codeview::MethodKind Virtuality) { + VirtualityCode = getVirtualityCode(Virtuality); + } + // DWARF Extern Codes. StringRef externalString() const; Index: llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h +++ llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h @@ -135,6 +135,12 @@ assert(Scope && Scope->isCompileUnit() && "Scope is not a compile unit"); CompileUnit = static_cast(Scope); } + void setCompileUnitCPUType(codeview::CPUType Type) { + CompileUnit->setCPUType(Type); + } + codeview::CPUType getCompileUnitCPUType() { + return CompileUnit->getCPUType(); + } public: // Access to the scopes root. 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 @@ -409,6 +409,9 @@ // Compilation directory name. size_t CompilationDirectoryIndex = 0; + // Used by the CodeView Reader. + codeview::CPUType CompilationCPUType = codeview::CPUType::X64; + // Keep record of elements. They are needed at the compilation unit level // to print the summary at the end of the printing. LVCounter Allocated; @@ -541,6 +544,9 @@ ProducerIndex = getStringPool().getIndex(ProducerName); } + void setCPUType(codeview::CPUType Type) { CompilationCPUType = Type; } + codeview::CPUType getCPUType() { return CompilationCPUType; } + // Record DWARF tags. void addDebugTag(dwarf::Tag Target, LVOffset Offset); // Record elements with invalid offsets. @@ -784,6 +790,10 @@ FileFormatNameIndex = getStringPool().getIndex(FileFormatName); } + // The CodeView Reader uses scoped names. Recursively transform the + // element name to use just the most inner component. + void transformScopedName(); + // Process the collected location, ranges and calculate coverage. void processRangeInformation(); 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 @@ -41,6 +41,11 @@ } }; +using LVStringRefs = std::vector; +using LVLexicalComponent = std::tuple; +using LVLexicalIndex = + std::tuple; + // Used to record specific characteristics about the objects. template class LVProperties { SmallBitVector Bits = SmallBitVector(static_cast(T::LastEntry) + 1); @@ -240,6 +245,15 @@ return (Twine("'") + Twine(Name1) + Twine(Name2) + Twine("'")).str(); } +// The given string represents a symbol or type name with optional enclosing +// scopes, such as: name, name<..>, scope::name, scope::..::name, etc. +// The string can have multiple references to template instantiations. +// It returns the inner most component. +LVLexicalComponent getInnerComponent(StringRef Name); +LVStringRefs getAllLexicalComponents(StringRef Name); +std::string getScopedName(const LVStringRefs &Components, + StringRef BaseName = {}); + // Return a pathname composed by: parent_path(To) / filename(From). // This is useful when a type server (PDB file associated with an object // file or a precompiled header file) has been moved from its original Index: llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h +++ llvm/include/llvm/DebugInfo/LogicalView/LVReaderHandler.h @@ -59,6 +59,8 @@ object::MachOUniversalBinary &Mach); Error handleObject(LVReaders &Readers, StringRef Filename, object::Binary &Binary); + Error handleObject(LVReaders &Readers, StringRef Filename, StringRef Buffer, + StringRef ExePath); Error createReader(StringRef Filename, LVReaders &Readers, PdbOrObj &Input, StringRef FileFormatName, StringRef ExePath = {}); Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h =================================================================== --- llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h @@ -24,6 +24,7 @@ #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/TargetRegistry.h" +#include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" namespace llvm { @@ -69,6 +70,12 @@ // Function names extracted from the object symbol table. LVSymbolTable SymbolTable; + // It contains the LVLineDebug elements representing the inlined logical + // lines for the current compile unit, created by parsing the CodeView + // S_INLINESITE symbol annotation data. + using LVInlineeLine = std::map; + LVInlineeLine CUInlineeLines; + // Instruction lines for a logical scope. These instructions are fetched // during its merge with the debug lines. using LVInstructions = std::map; @@ -134,6 +141,8 @@ LVAddress LowerAddress, LVAddress UpperAddress); LVRange *getSectionRanges(LVSectionIndex SectionIndex); + void includeInlineeLines(LVSectionIndex SectionIndex, LVScope *Function); + Error createInstructions(); Error createInstructions(LVScope *Function, LVSectionIndex SectionIndex); Error createInstructions(LVScope *Function, LVSectionIndex SectionIndex, @@ -155,6 +164,19 @@ delete Entry.second; } +public: + void addInlineeLines(LVScope *Scope, LVLines &Lines) { + LVLines *InlineeLines = new LVLines(); + *InlineeLines = std::move(Lines); + CUInlineeLines.emplace(Scope, InlineeLines); + } + + // Convert Segment::Offset pair to absolute address. + LVAddress linearAddress(uint16_t Segment, uint32_t Offset, + LVAddress Addendum = 0) { + return ImageBaseAddress + (Segment * VirtualAddress) + Offset + Addendum; + } + public: void addToSymbolTable(StringRef Name, LVScope *Function, LVSectionIndex SectionIndex = 0); Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h @@ -0,0 +1,238 @@ +//===-- LVCodeViewReader.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 LVCodeViewReader class, which is used to describe a +// debug information (COFF) reader. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H +#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H + +#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Support/BinaryByteStream.h" +#include "llvm/Support/BinaryItemStream.h" +#include "llvm/Support/BinaryStreamArray.h" + +namespace llvm { +template <> struct BinaryItemTraits { + static size_t length(const codeview::CVType &Item) { return Item.length(); } + static ArrayRef bytes(const codeview::CVType &Item) { + return Item.data(); + } +}; +} // namespace llvm + +namespace llvm { +namespace codeview { +class LazyRandomTypeCollection; +} +namespace object { +struct coff_section; +} +namespace pdb { +class SymbolGroup; +} +namespace logicalview { + +class LVElement; +class LVLine; +class LVScope; +class LVScopeCompileUnit; +class LVSymbol; +class LVType; +class LVTypeVisitor; +class LVSymbolVisitor; +class LVSymbolVisitorDelegate; + +using LVNames = SmallVector; + +// The ELF reader use the DWARF constants to create the logical elements. +// The DW_TAG_* and DW_AT_* are used to select the logical object and to +// set specific attributes, such as name, type, etc. +// As the CodeView constants are different to the DWARF constants, the +// CodeView reader will map them to the DWARF ones. + +class LVCodeViewReader final : public LVBinaryReader { + friend class LVTypeVisitor; + friend class LVSymbolVisitor; + friend class LVSymbolVisitorDelegate; + + using LVModules = std::vector; + LVModules Modules; + + // Encapsulates access to the input file and any dependent type server, + // including any precompiled header object. + llvm::pdb::InputFile Input; + std::shared_ptr TypeServer; + std::shared_ptr PrecompHeader; + + // Persistance data when loading a type server. + ErrorOr> BuffOrErr = nullptr; + std::unique_ptr MemBuffer; + std::unique_ptr Session; + std::unique_ptr PdbSession; + + // Persistance data when loading a precompiled header. + BumpPtrAllocator BuilderAllocator; + std::unique_ptr Builder; + std::unique_ptr> ItemStream; + std::unique_ptr ReaderPrecomp; + std::vector TypeArray; + CVTypeArray TypeStream; + CVTypeArray CVTypesPrecomp; + + // Persistance data when loading an executable file. + std::unique_ptr BinaryBuffer; + std::unique_ptr BinaryExecutable; + + Error loadTargetInfo(const object::ObjectFile &Obj); + Error loadTargetInfo(const llvm::pdb::PDBFile &Pdb); + + void mapRangeAddress(const object::ObjectFile &Obj, + const object::SectionRef &Section, + bool IsComdat) override; + + llvm::object::COFFObjectFile &getObj() { return Input.obj(); } + llvm::pdb::PDBFile &getPdb() { return Input.pdb(); } + bool isObj() const { return Input.isObj(); } + bool isPdb() const { return Input.isPdb(); } + StringRef getFileName() { return Input.getFilePath(); } + + // Pathname to executable image. + std::string ExePath; + + LVOffset CurrentOffset = 0; + int32_t CurrentModule = -1; + + using RelocMapTy = DenseMap>; + RelocMapTy RelocMap; + + // Object files have only one type stream that contains both types and ids. + // Precompiled header objects don't contain an IPI stream. Use the TPI. + LazyRandomTypeCollection &types() { + return TypeServer ? TypeServer->types() + : (PrecompHeader ? *PrecompHeader : Input.types()); + } + LazyRandomTypeCollection &ids() { + return TypeServer ? TypeServer->ids() + : (PrecompHeader ? *PrecompHeader : Input.ids()); + } + + LVLogicalVisitor LogicalVisitor; + + Expected + getFileNameForFileOffset(uint32_t FileOffset, + const llvm::pdb::SymbolGroup *SG = nullptr); + void printRelocatedField(StringRef Label, + const llvm::object::coff_section *CoffSection, + uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym); + + Error printFileNameForOffset(StringRef Label, uint32_t FileOffset, + const llvm::pdb::SymbolGroup *SG = nullptr); + + Error loadPrecompiledObject(PrecompRecord &Precomp, CVTypeArray &CVTypesObj); + Error loadTypeServer(TypeServer2Record &TS); + Error traverseTypes(llvm::pdb::PDBFile &Pdb, LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids); + + Error collectInlineeInfo(DebugInlineeLinesSubsectionRef &Lines, + const llvm::pdb::SymbolGroup *SG = nullptr); + + void cacheRelocations(); + Error resolveSymbol(const llvm::object::coff_section *CoffSection, + uint64_t Offset, llvm::object::SymbolRef &Sym); + Error resolveSymbolName(const llvm::object::coff_section *CoffSection, + uint64_t Offset, StringRef &Name); + Error traverseTypeSection(StringRef SectionName, + const llvm::object::SectionRef &Section); + Error traverseSymbolSection(StringRef SectionName, + const llvm::object::SectionRef &Section); + Error traverseInlineeLines(StringRef Subsection); + + DebugChecksumsSubsectionRef CVFileChecksumTable; + DebugStringTableSubsectionRef CVStringTable; + + Error traverseSymbolsSubsection(StringRef Subsection, + const llvm::object::SectionRef &Section, + StringRef SectionContents); + + /// Given a .debug$S section, find the string table and file checksum table. + /// This function taken from (COFFDumper.cpp). + /// It can be moved to the COFF library. + Error initializeFileAndStringTables(BinaryStreamReader &Reader); + + Error createLines(const FixedStreamArray &LineNumbers, + LVAddress Addendum, uint32_t Segment, uint32_t Begin, + uint32_t Size, uint32_t NameIndex, + const llvm::pdb::SymbolGroup *SG = nullptr); + Error createScopes(llvm::object::COFFObjectFile &Obj); + Error createScopes(llvm::pdb::PDBFile &Pdb); + Error processModule(); + +protected: + Error createScopes() override; + void sortScopes() override; + +public: + LVCodeViewReader() = delete; + LVCodeViewReader(StringRef Filename, StringRef FileFormatName, + llvm::object::COFFObjectFile &Obj, ScopedPrinter &W, + StringRef ExePath) + : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::COFF), + Input(&Obj), ExePath(ExePath), LogicalVisitor(this, W, Input) {} + LVCodeViewReader(StringRef Filename, StringRef FileFormatName, + llvm::pdb::PDBFile &Pdb, ScopedPrinter &W, StringRef ExePath) + : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::COFF), + Input(&Pdb), ExePath(ExePath), LogicalVisitor(this, W, Input) {} + LVCodeViewReader(const LVCodeViewReader &) = delete; + LVCodeViewReader &operator=(const LVCodeViewReader &) = delete; + ~LVCodeViewReader() = default; + + void getLinkageName(const llvm::object::coff_section *CoffSection, + uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym); + + void addModule(LVScope *Scope) { Modules.push_back(Scope); } + LVScope *getScopeForModule(uint32_t Modi) { + return Modi >= Modules.size() ? nullptr : Modules[Modi]; + } + + // Get the string representation for the CodeView symbols. + static std::string getSymbolKindName(SymbolKind Kind); + static std::string formatRegisterId(RegisterId Register, CPUType CPU); + + std::string getRegisterName(LVSmall Opcode, uint64_t Operands[2]) override; + + bool isSystemEntry(LVElement *Element, StringRef Name) const override; + +public: + void print(raw_ostream &OS) const; + void printRecords(raw_ostream &OS) const override { + LogicalVisitor.printRecords(OS); + }; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + void dump() const { print(dbgs()); } +#endif +}; + +} // end namespace logicalview +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWREADER_H Index: llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h =================================================================== --- /dev/null +++ llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h @@ -0,0 +1,481 @@ +//===-- LVCodeViewVisitor.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 LVCodeViewVisitor class, which is used to describe a +// debug information (CodeView) visitor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H +#define LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/iterator.h" +#include "llvm/DebugInfo/CodeView/SymbolDumpDelegate.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVBinaryReader.h" +#include "llvm/DebugInfo/PDB/Native/InputFile.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { +namespace logicalview { + +using namespace llvm::codeview; + +class LVCodeViewReader; +class LVLogicalVisitor; +struct LVShared; + +class LVTypeVisitor final : public TypeVisitorCallbacks { + ScopedPrinter &W; + LVLogicalVisitor *LogicalVisitor; + LazyRandomTypeCollection &Types; + LazyRandomTypeCollection &Ids; + uint32_t StreamIdx; + LVShared *Shared = nullptr; + + // In a PDB, a type index may refer to a type (TPI) or an item ID (IPI). + // In a COFF or PDB (/Z7), the type index always refer to a type (TPI). + // When creating logical elements, we must access the correct element + // table, while searching for a type index. + bool HasIds = false; + + // Current type index during the types traversal. + TypeIndex CurrentTypeIndex = TypeIndex::None(); + + void printTypeIndex(StringRef FieldName, TypeIndex TI, + uint32_t StreamIdx) const; + +public: + LVTypeVisitor(ScopedPrinter &W, LVLogicalVisitor *LogicalVisitor, + LazyRandomTypeCollection &Types, LazyRandomTypeCollection &Ids, + uint32_t StreamIdx, LVShared *Shared) + : TypeVisitorCallbacks(), W(W), LogicalVisitor(LogicalVisitor), + Types(Types), Ids(Ids), StreamIdx(StreamIdx), Shared(Shared) { + HasIds = &Types != &Ids; + } + + Error visitTypeBegin(CVType &Record) override; + Error visitTypeBegin(CVType &Record, TypeIndex TI) override; + Error visitMemberBegin(CVMemberRecord &Record) override; + Error visitMemberEnd(CVMemberRecord &Record) override; + Error visitUnknownMember(CVMemberRecord &Record) override; + + Error visitKnownRecord(CVType &Record, BuildInfoRecord &Args) override; + Error visitKnownRecord(CVType &Record, ClassRecord &Class) override; + Error visitKnownRecord(CVType &Record, EnumRecord &Enum) override; + Error visitKnownRecord(CVType &Record, FuncIdRecord &Func) override; + Error visitKnownRecord(CVType &Record, ProcedureRecord &Proc) override; + Error visitKnownRecord(CVType &Record, StringIdRecord &String) override; + Error visitKnownRecord(CVType &Record, UdtSourceLineRecord &Line) override; + Error visitKnownRecord(CVType &Record, UnionRecord &Union) override; + Error visitUnknownType(CVType &Record) override; +}; + +class LVSymbolVisitorDelegate final : public SymbolVisitorDelegate { +public: + LVSymbolVisitorDelegate(LVCodeViewReader *Reader, + const llvm::object::SectionRef &Section, + const llvm::object::COFFObjectFile *Obj, + StringRef SectionContents) + : Reader(Reader), SectionContents(SectionContents) { + CoffSection = Obj->getCOFFSection(Section); + } + + uint32_t getRecordOffset(BinaryStreamReader Reader) override { + ArrayRef Data; + if (Error Err = Reader.readLongestContiguousChunk(Data)) { + llvm::consumeError(std::move(Err)); + return 0; + } + return Data.data() - SectionContents.bytes_begin(); + } + + void printRelocatedField(StringRef Label, uint32_t RelocOffset, + uint32_t Offset, StringRef *RelocSym = nullptr); + + void getLinkageName(uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym = nullptr); + + StringRef getFileNameForFileOffset(uint32_t FileOffset) override; + DebugStringTableSubsectionRef getStringTable() override; + +private: + LVCodeViewReader *Reader = nullptr; + const llvm::object::coff_section *CoffSection; + StringRef SectionContents; +}; + +class LVElement; +class LVScope; +class LVSymbol; +class LVType; + +// Current elements during the processing of a RecordType or RecordSymbol. +extern LVElement *CurrentElement; +extern LVScope *CurrentScope; +extern LVSymbol *CurrentSymbol; +extern LVType *CurrentType; + +// Visitor for CodeView symbol streams found in COFF object files and PDB files. +class LVSymbolVisitor final : public SymbolVisitorCallbacks { + LVCodeViewReader *Reader = nullptr; + ScopedPrinter &W; + LVLogicalVisitor *LogicalVisitor; + LazyRandomTypeCollection &Types; + LazyRandomTypeCollection &Ids; + LVSymbolVisitorDelegate *ObjDelegate; + LVShared *Shared = nullptr; + + // Symbol offset when processing PDB streams. + uint32_t CurrentOffset = 0; + // Current object name collected from S_OBJNAME. + StringRef CurrentObjectName; + // Last symbol processed by S_LOCAL. + LVSymbol *LocalSymbol = nullptr; + + bool HasIds = false; + bool InFunctionScope = false; + bool IsCompileUnit = false; + + // Register for the locals and parameters symbols in the current frame. + RegisterId LocalFrameRegister; + RegisterId ParamFrameRegister; + + void printLocalVariableAddrRange(const LocalVariableAddrRange &Range, + uint32_t RelocationOffset); + void printLocalVariableAddrGap(ArrayRef Gaps); + void printTypeIndex(StringRef FieldName, TypeIndex TI) const; + + // Return true if this symbol is a Compile Unit. + bool symbolIsCompileUnit(SymbolKind Kind) { + switch (Kind) { + case SymbolKind::S_COMPILE2: + case SymbolKind::S_COMPILE3: + return true; + default: + return false; + } + } + + // Determine symbol kind (local or parameter). + void determineSymbolKind(LVSymbol *Symbol, RegisterId Register) { + if (Register == LocalFrameRegister) { + Symbol->setIsVariable(); + return; + } + if (Register == ParamFrameRegister) { + Symbol->setIsParameter(); + return; + } + // Assume is a variable. + Symbol->setIsVariable(); + } + +public: + LVSymbolVisitor(LVCodeViewReader *Reader, ScopedPrinter &W, + LVLogicalVisitor *LogicalVisitor, + LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids, + LVSymbolVisitorDelegate *ObjDelegate, LVShared *Shared) + : Reader(Reader), W(W), LogicalVisitor(LogicalVisitor), Types(Types), + Ids(Ids), ObjDelegate(ObjDelegate), Shared(Shared) { + HasIds = &Types != &Ids; + } + + Error visitSymbolBegin(CVSymbol &Record) override; + Error visitSymbolBegin(CVSymbol &Record, uint32_t Offset) override; + Error visitSymbolEnd(CVSymbol &Record) override; + Error visitUnknownSymbol(CVSymbol &Record) override; + + Error visitKnownRecord(CVSymbol &Record, BlockSym &Block) override; + Error visitKnownRecord(CVSymbol &Record, BPRelativeSym &Local) override; + Error visitKnownRecord(CVSymbol &Record, BuildInfoSym &BuildInfo) override; + Error visitKnownRecord(CVSymbol &Record, Compile2Sym &Compile2) override; + Error visitKnownRecord(CVSymbol &Record, Compile3Sym &Compile3) override; + Error visitKnownRecord(CVSymbol &Record, ConstantSym &Constant) override; + Error visitKnownRecord(CVSymbol &Record, DataSym &Data) override; + Error visitKnownRecord(CVSymbol &Record, + DefRangeFramePointerRelFullScopeSym + &DefRangeFramePointerRelFullScope) override; + Error visitKnownRecord( + CVSymbol &Record, + DefRangeFramePointerRelSym &DefRangeFramePointerRel) override; + Error visitKnownRecord(CVSymbol &Record, + DefRangeRegisterRelSym &DefRangeRegisterRel) override; + Error visitKnownRecord(CVSymbol &Record, + DefRangeRegisterSym &DefRangeRegister) override; + Error visitKnownRecord( + CVSymbol &Record, + DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) override; + Error visitKnownRecord(CVSymbol &Record, + DefRangeSubfieldSym &DefRangeSubfield) override; + Error visitKnownRecord(CVSymbol &Record, DefRangeSym &DefRange) override; + Error visitKnownRecord(CVSymbol &Record, FrameProcSym &FrameProc) override; + Error visitKnownRecord(CVSymbol &Record, InlineSiteSym &InlineSite) override; + Error visitKnownRecord(CVSymbol &Record, LocalSym &Local) override; + Error visitKnownRecord(CVSymbol &Record, ObjNameSym &ObjName) override; + Error visitKnownRecord(CVSymbol &Record, ProcSym &Proc) override; + Error visitKnownRecord(CVSymbol &Record, RegRelativeSym &Local) override; + Error visitKnownRecord(CVSymbol &Record, ScopeEndSym &ScopeEnd) override; + Error visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) override; + Error visitKnownRecord(CVSymbol &Record, UDTSym &UDT) override; + Error visitKnownRecord(CVSymbol &Record, UsingNamespaceSym &UN) override; +}; + +// Visitor for CodeView types and symbols to populate elements. +class LVLogicalVisitor final { + LVCodeViewReader *Reader = nullptr; + ScopedPrinter &W; + + // Encapsulates access to the input file and any dependent type server, + // including any precompiled header object. + llvm::pdb::InputFile &Input; + std::shared_ptr TypeServer = nullptr; + std::shared_ptr PrecompHeader = nullptr; + + LVShared *Shared = nullptr; + + // Object files have only one type stream that contains both types and ids. + // Precompiled header objects don't contain an IPI stream. Use the TPI. + LazyRandomTypeCollection &types() { + return TypeServer ? TypeServer->types() + : (PrecompHeader ? *PrecompHeader : Input.types()); + } + LazyRandomTypeCollection &ids() { + return TypeServer ? TypeServer->ids() + : (PrecompHeader ? *PrecompHeader : Input.ids()); + } + + using LVScopeStack = std::stack; + LVScopeStack ScopeStack; + LVScope *ReaderParent = nullptr; + LVScope *ReaderScope = nullptr; + bool InCompileUnitScope = false; + + // Allow processing of argument list. + bool ProcessArgumentList = false; + StringRef OverloadedMethodName; + std::string CompileUnitName; + + // Inlined functions source information. + using LVInlineeEntry = std::pair; + using LVInlineeInfo = std::map; + LVInlineeInfo InlineeInfo; + + Error visitFieldListMemberStream(TypeIndex TI, LVElement *Element, + ArrayRef FieldList); + + LVType *createBaseType(TypeIndex TI, StringRef TypeName); + LVType *createPointerType(TypeIndex TI, StringRef TypeName); + LVSymbol *createParameter(TypeIndex TI, StringRef Name, LVScope *Parent); + LVSymbol *createParameter(LVElement *Element, StringRef Name, + LVScope *Parent); + void createDataMember(CVMemberRecord &Record, LVScope *Parent, StringRef Name, + TypeIndex Type, MemberAccess Access); + void createParents(StringRef ScopedName, LVElement *Element); + +public: + LVLogicalVisitor(LVCodeViewReader *Reader, ScopedPrinter &W, + llvm::pdb::InputFile &Input); + ~LVLogicalVisitor(); + + // Input source in the case of type server or precompiled header. + void setInput(std::shared_ptr TypeServer) { + this->TypeServer = TypeServer; + } + void setInput(std::shared_ptr PrecompHeader) { + this->PrecompHeader = PrecompHeader; + } + + void addInlineeInfo(TypeIndex TI, uint32_t LineNumber, StringRef Filename) { + InlineeInfo.emplace(std::piecewise_construct, std::forward_as_tuple(TI), + std::forward_as_tuple(LineNumber, Filename)); + } + + void printTypeIndex(StringRef FieldName, TypeIndex TI, uint32_t StreamIdx); + void printMemberAttributes(MemberAttributes Attrs); + void printMemberAttributes(MemberAccess Access, MethodKind Kind, + MethodOptions Options); + + LVElement *createElement(TypeLeafKind Kind); + LVElement *createElement(SymbolKind Kind); + LVElement *createElement(TypeIndex TI, TypeLeafKind Kind); + + // Break down the annotation byte code and calculate code and line offsets. + Error inlineSiteAnnotation(LVScope *AbstractFunction, + LVScope *InlinedFunction, + InlineSiteSym &InlineSite); + + void pushScope(LVScope *Scope) { + ScopeStack.push(ReaderParent); + ReaderParent = ReaderScope; + ReaderScope = Scope; + } + void popScope() { + ReaderScope = ReaderParent; + ReaderParent = ScopeStack.top(); + ScopeStack.pop(); + } + void closeScope() { + if (InCompileUnitScope) { + InCompileUnitScope = false; + popScope(); + } + } + void setRoot(LVScope *Root) { ReaderScope = Root; } + + void addElement(LVScope *Scope, bool IsCompileUnit); + void addElement(LVSymbol *Symbol); + void addElement(LVType *Type); + + std::string getCompileUnitName() { return CompileUnitName; } + void setCompileUnitName(std::string Name) { + CompileUnitName = std::move(Name); + } + +public: + LVElement *getElement(uint32_t StreamIdx, TypeIndex TI, + LVScope *Parent = nullptr); + LVShared *getShared() { return Shared; } + + LVScope *getReaderScope() const { return ReaderScope; } + + void printTypeBegin(CVType &Record, TypeIndex TI, LVElement *Element, + uint32_t StreamIdx); + void printTypeEnd(CVType &Record); + void printMemberBegin(CVMemberRecord &Record, TypeIndex TI, + LVElement *Element, uint32_t StreamIdx); + void printMemberEnd(CVMemberRecord &Record); + + void startProcessArgumentList() { ProcessArgumentList = true; } + void stopProcessArgumentList() { ProcessArgumentList = false; } + + void processFiles(); + void processLines(); + void processNamespaces(); + + void printRecords(raw_ostream &OS) const; + +public: + Error visitUnknownType(CVType &Record, TypeIndex TI); + Error visitKnownRecord(CVType &Record, ArgListRecord &Args, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, ArrayRecord &AT, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, BitFieldRecord &BF, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, BuildInfoRecord &BI, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, ClassRecord &Class, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, EnumRecord &Enum, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, FieldListRecord &FieldList, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, FuncIdRecord &Func, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, LabelRecord &LR, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, ModifierRecord &Mod, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, MemberFuncIdRecord &Id, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, MemberFunctionRecord &MF, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, MethodOverloadListRecord &Overloads, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, PointerRecord &Ptr, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, ProcedureRecord &Proc, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, UnionRecord &Union, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, TypeServer2Record &TS, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, VFTableRecord &VFT, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, VFTableShapeRecord &Shape, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, StringListRecord &Strings, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, StringIdRecord &String, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, UdtSourceLineRecord &SourceLine, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, UdtModSourceLineRecord &ModSourceLine, + TypeIndex TI, LVElement *Element); + Error visitKnownRecord(CVType &Record, PrecompRecord &Precomp, TypeIndex TI, + LVElement *Element); + Error visitKnownRecord(CVType &Record, EndPrecompRecord &EndPrecomp, + TypeIndex TI, LVElement *Element); + + Error visitUnknownMember(CVMemberRecord &Record, TypeIndex TI); + Error visitKnownMember(CVMemberRecord &Record, BaseClassRecord &Base, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, DataMemberRecord &Field, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, EnumeratorRecord &Enum, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, ListContinuationRecord &Cont, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, NestedTypeRecord &Nested, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, OneMethodRecord &Method, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, OverloadedMethodRecord &Method, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, StaticDataMemberRecord &Field, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, VFPtrRecord &VFTable, + TypeIndex TI, LVElement *Element); + Error visitKnownMember(CVMemberRecord &Record, VirtualBaseClassRecord &Base, + TypeIndex TI, LVElement *Element); + + template + Error visitKnownMember(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks, TypeIndex TI, + LVElement *Element) { + TypeRecordKind RK = static_cast(Record.Kind); + T KnownRecord(RK); + if (Error Err = Callbacks.visitKnownMember(Record, KnownRecord)) + return Err; + if (Error Err = visitKnownMember(Record, KnownRecord, TI, Element)) + return Err; + return Error::success(); + } + + template + Error visitKnownRecord(CVType &Record, TypeIndex TI, LVElement *Element) { + TypeRecordKind RK = static_cast(Record.kind()); + T KnownRecord(RK); + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(Record), KnownRecord)) + return Err; + if (Error Err = visitKnownRecord(Record, KnownRecord, TI, Element)) + return Err; + return Error::success(); + } + + Error visitMemberRecord(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks, TypeIndex TI, + LVElement *Element); + Error finishVisitation(CVType &Record, TypeIndex TI, LVElement *Element); +}; + +} // namespace logicalview +} // namespace llvm + +#endif // LLVM_DEBUGINFO_LOGICALVIEW_READERS_CODEVIEWVISITOR_H Index: llvm/lib/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- llvm/lib/DebugInfo/LogicalView/CMakeLists.txt +++ llvm/lib/DebugInfo/LogicalView/CMakeLists.txt @@ -33,6 +33,8 @@ add_lv_impl_folder(Readers LVReaderHandler.cpp Readers/LVBinaryReader.cpp + Readers/LVCodeViewReader.cpp + Readers/LVCodeViewVisitor.cpp Readers/LVELFReader.cpp ) Index: llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVElement.cpp @@ -17,6 +17,7 @@ #include "llvm/DebugInfo/LogicalView/Core/LVType.h" using namespace llvm; +using namespace llvm::codeview; using namespace llvm::logicalview; #define DEBUG_TYPE "Element" @@ -103,6 +104,14 @@ FilenameIndex = getStringIndex(Filename); } +void LVElement::setInnerComponent(StringRef Name) { + if (Name.size()) { + StringRef InnerComponent; + std::tie(std::ignore, InnerComponent) = getInnerComponent(Name); + setName(InnerComponent); + } +} + // Return the string representation of a DIE offset. std::string LVElement::typeOffsetAsString() const { if (options().getAttributeOffset()) { @@ -126,6 +135,19 @@ } } +uint32_t LVElement::getAccessibilityCode(MemberAccess Access) { + switch (Access) { + case MemberAccess::Private: + return dwarf::DW_ACCESS_private; + case MemberAccess::Protected: + return dwarf::DW_ACCESS_protected; + case MemberAccess::Public: + return dwarf::DW_ACCESS_public; + default: + return 0; + } +} + StringRef LVElement::externalString() const { return getIsExternal() ? "extern" : StringRef(); } @@ -160,6 +182,21 @@ } } +uint32_t LVElement::getVirtualityCode(MethodKind Virtuality) { + switch (Virtuality) { + case MethodKind::Virtual: + return dwarf::DW_VIRTUALITY_virtual; + case MethodKind::PureVirtual: + return dwarf::DW_VIRTUALITY_pure_virtual; + case MethodKind::IntroducingVirtual: + case MethodKind::PureIntroducingVirtual: + // No direct equivalents in DWARF. Assume Virtual. + return dwarf::DW_VIRTUALITY_virtual; + default: + return 0; + } +} + void LVElement::resolve() { if (getIsResolved()) return; @@ -205,6 +242,7 @@ // Generate a name for unnamed elements. void LVElement::generateName(std::string &Prefix) const { + // CodeView elements that are not referenced and not finalized. LVScope *Scope = getParentScope(); if (!Scope) return; Index: llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVReader.cpp @@ -179,6 +179,9 @@ // Get the filename for given object. StringRef LVReader::getFilename(LVObject *Object, size_t Index) const { + // The current CodeView Reader implementation does not have support for + // multiple compile units. Until a proper offset calculation, check only + // in the current compile unit. if (CompileUnits.size()) { // Get Compile Unit for the given object. LVCompileUnits::const_iterator Iter = Index: llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVScope.cpp @@ -598,6 +598,10 @@ // split context, then switch to the reader output stream. raw_ostream *StreamSplit = &OS; + // Ignore the CU generated by the VS toolchain, when compiling to PDB. + if (getIsSystem() && !options().getAttributeSystem()) + return Error::success(); + // 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()) { @@ -1788,6 +1792,8 @@ // DW_AT_external DW_FORM_flag_present // 00000070 DW_TAG_subprogram "bar" // DW_AT_specification DW_FORM_ref4 0x00000048 + // The CodeView does not include any information at the class level to + // mark the member function as external. // If there is a reference linking the declaration and definition, mark // the definition as extern, to facilitate the logical view comparison. if (getHasReferenceSpecification()) { @@ -2023,6 +2029,28 @@ } } +void LVScopeRoot::transformScopedName() { + // Recursively transform all names. + std::function TraverseScope = [&](LVScope *Parent) { + auto Traverse = [&](const auto *Set) { + if (Set) + for (const auto &Entry : *Set) + Entry->setInnerComponent(); + }; + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + Scope->setInnerComponent(); + TraverseScope(Scope); + } + Traverse(Parent->getSymbols()); + Traverse(Parent->getTypes()); + Traverse(Parent->getLines()); + }; + + // Start traversing the scopes root and transform the element name. + TraverseScope(this); +} + bool LVScopeRoot::equals(const LVScope *Scope) const { return LVScope::equals(Scope); } Index: llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVSupport.cpp @@ -54,3 +54,113 @@ std::replace(Name.begin(), Name.end(), ' ', '_'); return Name; } + +using LexicalEntry = std::pair; +using LexicalIndexes = SmallVector; + +static LexicalIndexes getAllLexicalIndexes(StringRef Name) { + if (Name.empty()) + return {}; + + bool InTemplate = false; + size_t AngleCount = 0; + size_t CharsSeen = 0; + size_t ColonSeen = 0; + size_t Current = 0; + + LexicalIndexes Indexes; + +#ifndef NDEBUG + auto PrintLexicalEntry = [&]() { + LexicalEntry Entry = Indexes.back(); + llvm::dbgs() << formatv( + "'{0}:{1}', '{2}'\n", Entry.first, Entry.second, + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + }; +#endif + + size_t Length = Name.size(); + for (size_t Index = 0; Index < Length; ++Index) { + LLVM_DEBUG({ + llvm::dbgs() << formatv("Index: '{0}', Char: '{1}'\n", Index, + Name[Index]); + }); + switch (Name[Index]) { + case '<': + ++AngleCount; + InTemplate = true; + break; + case '>': + --AngleCount; + break; + case ':': + ++ColonSeen; + break; + } + ++CharsSeen; + if (ColonSeen == 2) { + if (!InTemplate) { + Indexes.push_back(LexicalEntry(Current, Index - 2)); + Current = Index + 1; + CharsSeen = 0; + LLVM_DEBUG({ PrintLexicalEntry(); }); + } + ColonSeen = 0; + continue; + } + if (InTemplate && !AngleCount) + InTemplate = false; + } + + // Store last component. + Indexes.push_back(LexicalEntry(Current, Length - 1)); + LLVM_DEBUG({ PrintLexicalEntry(); }); + return Indexes; +} + +LVLexicalComponent llvm::logicalview::getInnerComponent(StringRef Name) { + if (Name.empty()) + return {}; + + LexicalIndexes Indexes = getAllLexicalIndexes(Name); + if (Indexes.size() == 1) + return std::make_tuple(StringRef(), Name); + + LexicalEntry BeginEntry = Indexes.front(); + LexicalEntry EndEntry = Indexes[Indexes.size() - 2]; + StringRef Outer = + Name.substr(BeginEntry.first, EndEntry.second - BeginEntry.first + 1); + + LexicalEntry LastEntry = Indexes.back(); + StringRef Inner = + Name.substr(LastEntry.first, LastEntry.second - LastEntry.first + 1); + + return std::make_tuple(Outer, Inner); +} + +LVStringRefs llvm::logicalview::getAllLexicalComponents(StringRef Name) { + if (Name.empty()) + return {}; + + LexicalIndexes Indexes = getAllLexicalIndexes(Name); + LVStringRefs Components; + for (const LexicalEntry &Entry : Indexes) + Components.push_back( + Name.substr(Entry.first, Entry.second - Entry.first + 1)); + + return Components; +} + +std::string llvm::logicalview::getScopedName(const LVStringRefs &Components, + StringRef BaseName) { + if (Components.empty()) + return {}; + std::string Name(BaseName); + raw_string_ostream Stream(Name); + if (BaseName.size()) + Stream << "::"; + Stream << Components[0]; + for (LVStringRefs::size_type Index = 1; Index < Components.size(); ++Index) + Stream << "::" << Components[Index]; + return Name; +} Index: llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp +++ llvm/lib/DebugInfo/LogicalView/Core/LVType.cpp @@ -328,6 +328,13 @@ } void LVTypeDefinition::resolveExtra() { + // In the case of CodeView, the MSVC toolset generates a series of typedefs + // that refer to internal runtime structures, that we do not process. Those + // typedes are marked as 'system'. They have an associated logical type, + // but the underlying type always is null. + if (getIsSystem()) + return; + // Set the reference to the typedef type. if (options().getAttributeUnderlying()) { setUnderlyingType(getUnderlyingType()); Index: llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp +++ llvm/lib/DebugInfo/LogicalView/LVReaderHandler.cpp @@ -11,8 +11,13 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/LogicalView/Core/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" #include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Object/COFF.h" using namespace llvm; using namespace llvm::object; @@ -44,9 +49,18 @@ auto CreateOneReader = [&]() -> LVReader * { if (Input.is()) { ObjectFile &Obj = *Input.get(); + if (Obj.isCOFF()) { + COFFObjectFile *COFF = dyn_cast(&Obj); + return new LVCodeViewReader(Filename, FileFormatName, *COFF, W, + ExePath); + } if (Obj.isELF() || Obj.isMachO()) return new LVELFReader(Filename, FileFormatName, Obj, W); } + if (Input.is()) { + PDBFile &Pdb = *Input.get(); + return new LVCodeViewReader(Filename, FileFormatName, Pdb, W, ExePath); + } return nullptr; }; @@ -84,8 +98,103 @@ return Error::success(); } +// Search for a matching executable image for the given PDB path. +static std::string searchForExe(const StringRef Path, + const StringRef Extension) { + SmallString<128> ExePath(Path); + llvm::sys::path::replace_extension(ExePath, Extension); + + std::unique_ptr Session; + if (Error Err = loadDataForEXE(PDB_ReaderType::Native, ExePath, Session)) { + consumeError(std::move(Err)); + return {}; + } + // We have a candidate for the executable image. + Expected PdbPathOrErr = NativeSession::searchForPdb({ExePath}); + if (!PdbPathOrErr) { + consumeError(PdbPathOrErr.takeError()); + return {}; + } + // Convert any Windows backslashes into forward slashes to get the path. + std::string ConvertedPath = sys::path::convert_to_slash( + PdbPathOrErr.get(), sys::path::Style::windows); + if (ConvertedPath == Path) + return std::string(ExePath); + + return {}; +} + +// Search for a matching object image for the given PDB path. +static std::string searchForObj(const StringRef Path, + const StringRef Extension) { + SmallString<128> ObjPath(Path); + llvm::sys::path::replace_extension(ObjPath, Extension); + if (llvm::sys::fs::exists(Twine(ObjPath))) { + ErrorOr> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(ObjPath); + if (!BuffOrErr) + return {}; + return std::string(ObjPath); + } + + return {}; +} + Error LVReaderHandler::handleBuffer(LVReaders &Readers, StringRef Filename, MemoryBufferRef Buffer, StringRef ExePath) { + // As PDB does not support the Binary interface, at this point we can check + // if the buffer corresponds to a PDB or PE file. + file_magic FileMagic = identify_magic(Buffer.getBuffer()); + if (FileMagic == file_magic::pdb) { + if (!ExePath.empty()) + return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath); + + // Search in the directory derivated from the given 'Filename' for a + // matching object file (.o, .obj, .lib) or a matching executable file + // (.exe/.dll) and try to create the reader based on the matched file. + // If no matching file is found then we load the original PDB file. + std::vector ExecutableExtensions = {"exe", "dll"}; + for (StringRef Extension : ExecutableExtensions) { + std::string ExecutableImage = searchForExe(Filename, Extension); + if (ExecutableImage.empty()) + continue; + if (Error Err = handleObject(Readers, Filename, Buffer.getBuffer(), + ExecutableImage)) { + consumeError(std::move(Err)); + continue; + } + return Error::success(); + } + + std::vector ObjectExtensions = {"o", "obj", "lib"}; + for (StringRef Extension : ObjectExtensions) { + std::string ObjectImage = searchForObj(Filename, Extension); + if (ObjectImage.empty()) + continue; + if (Error Err = handleFile(Readers, ObjectImage)) { + consumeError(std::move(Err)); + continue; + } + return Error::success(); + } + + // No matching executable/object image was found. Load the given PDB. + return handleObject(Readers, Filename, Buffer.getBuffer(), ExePath); + } + if (FileMagic == file_magic::pecoff_executable) { + // If we have a valid executable, try to find a matching PDB file. + Expected PdbPath = NativeSession::searchForPdb({Filename}); + if (errorToErrorCode(PdbPath.takeError())) { + std::string TheFilename(Filename); + return createStringError( + errc::not_supported, + "Binary object format in '%s' does not have debug info.", + TheFilename.c_str()); + } + // Process the matching PDB file and pass the executable filename. + return handleFile(Readers, PdbPath.get(), Filename); + } + Expected> BinOrErr = createBinary(Buffer); if (errorToErrorCode(BinOrErr.takeError())) { std::string TheFilename(Filename); @@ -154,6 +263,25 @@ return Error::success(); } +Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename, + StringRef Buffer, StringRef ExePath) { + std::unique_ptr Session; + if (Error Err = loadDataForPDB(PDB_ReaderType::Native, Filename, Session)) { + std::string TheFilename(Filename); + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + } + + std::unique_ptr PdbSession; + PdbSession.reset(static_cast(Session.release())); + PdbOrObj Input = &PdbSession->getPDBFile(); + StringRef FileFormatName; + size_t Pos = Buffer.find_first_of("\r\n"); + if (Pos) + FileFormatName = Buffer.substr(0, Pos - 1); + return createReader(Filename, Readers, Input, FileFormatName, ExePath); +} + Error LVReaderHandler::createReaders() { LLVM_DEBUG(dbgs() << "createReaders\n"); for (std::string &Object : Objects) { Index: llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp =================================================================== --- llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp +++ llvm/lib/DebugInfo/LogicalView/Readers/LVBinaryReader.cpp @@ -190,6 +190,61 @@ }); } +void LVBinaryReader::mapVirtualAddress(const object::COFFObjectFile &COFFObj) { + ErrorOr ImageBase = COFFObj.getImageBase(); + if (ImageBase) + ImageBaseAddress = ImageBase.get(); + + LLVM_DEBUG({ + dbgs() << "ImageBaseAddress: " << hexValue(ImageBaseAddress) << "\n"; + }); + + uint32_t Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_LNK_COMDAT; + + for (const object::SectionRef &Section : COFFObj.sections()) { + if (!Section.isText() || Section.isVirtual() || !Section.getSize()) + continue; + + const object::coff_section *COFFSection = COFFObj.getCOFFSection(Section); + VirtualAddress = COFFSection->VirtualAddress; + bool IsComdat = (COFFSection->Characteristics & Flags) == Flags; + + // Record section information required for symbol resolution. + // Note: The section index returned by 'getIndex()' is zero based. + Sections.emplace(Section.getIndex() + 1, Section); + addSectionAddress(Section); + + // Additional initialization on the specific object format. + mapRangeAddress(COFFObj, Section, IsComdat); + } + + LLVM_DEBUG({ + dbgs() << "\nSections Information:\n"; + for (LVSections::reference Entry : Sections) { + LVSectionIndex SectionIndex = Entry.first; + const object::SectionRef Section = Entry.second; + const object::coff_section *COFFSection = COFFObj.getCOFFSection(Section); + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + consumeError(SectionNameOrErr.takeError()); + dbgs() << "\nIndex: " << format_decimal(SectionIndex, 3) + << " Name: " << *SectionNameOrErr << "\n" + << "Size: " << hexValue(Section.getSize()) << "\n" + << "VirtualAddress: " << hexValue(VirtualAddress) << "\n" + << "SectionAddress: " << hexValue(Section.getAddress()) << "\n" + << "PointerToRawData: " << hexValue(COFFSection->PointerToRawData) + << "\n" + << "SizeOfRawData: " << hexValue(COFFSection->SizeOfRawData) + << "\n"; + } + dbgs() << "\nObject Section Information:\n"; + for (LVSectionAddresses::const_reference Entry : SectionAddresses) + dbgs() << "[" << hexValue(Entry.first) << ":" + << hexValue(Entry.first + Entry.second.getSize()) + << "] Size: " << hexValue(Entry.second.getSize()) << "\n"; + }); +} + Error LVBinaryReader::loadGenericTargetInfo(StringRef TheTriple, StringRef TheFeatures) { std::string TargetLookupError; @@ -760,6 +815,80 @@ } } +// Traverse the scopes for the given 'Function' looking for any inlined +// scopes with inlined lines, which are recorded in 'CUInlineeLines'. +void LVBinaryReader::includeInlineeLines(LVSectionIndex SectionIndex, + LVScope *Function) { + SmallVector InlineeIters; + std::function FindInlinedScopes = + [&](LVScope *Parent) { + if (const LVScopes *Scopes = Parent->getScopes()) + for (LVScope *Scope : *Scopes) { + LVInlineeLine::iterator Iter = CUInlineeLines.find(Scope); + if (Iter != CUInlineeLines.end()) + InlineeIters.push_back(Iter); + FindInlinedScopes(Scope); + } + }; + + // Find all inlined scopes for the given 'Function'. + FindInlinedScopes(Function); + for (LVInlineeLine::iterator InlineeIter : InlineeIters) { + LVScope *Scope = InlineeIter->first; + addToSymbolTable(Scope->getLinkageName(), Scope, SectionIndex); + + LVLines *InlineeLines = InlineeIter->second; + LLVM_DEBUG({ + dbgs() << "Inlined lines for: " << Scope->getName() << "\n"; + for (const LVLine *Line : *InlineeLines) + dbgs() << "[" << hexValue(Line->getAddress()) << "] " + << Line->getLineNumber() << "\n"; + dbgs() << format("Debug lines: %d\n", CULines.size()); + for (const LVLine *Line : CULines) + dbgs() << "Line address: " << hexValue(Line->getOffset()) << ", (" + << Line->getLineNumber() << ")\n"; + ; + }); + + // The inlined lines must be merged using its address, in order to keep + // the real order of the instructions. The inlined lines are mixed with + // the other non-inlined lines. + if (InlineeLines->size()) { + // First address of inlinee code. + uint64_t InlineeStart = (InlineeLines->front())->getAddress(); + LVLines::iterator Iter = std::find_if( + CULines.begin(), CULines.end(), [&](LVLine *Item) -> bool { + return Item->getAddress() == InlineeStart; + }); + if (Iter != CULines.end()) { + // 'Iter' points to the line where the inlined function is called. + // Emulate the DW_AT_call_line attribute. + Scope->setCallLineNumber((*Iter)->getLineNumber()); + // Mark the referenced line as the start of the inlined function. + // Skip the first line during the insertion, as the address and + // line number as the same. Otherwise we have to erase and insert. + (*Iter)->setLineNumber((*InlineeLines->begin())->getLineNumber()); + ++Iter; + CULines.insert(Iter, InlineeLines->begin() + 1, InlineeLines->end()); + } + } + + // Remove this set of lines from the container; each inlined function + // creates an unique set of lines. Remove only the created container. + CUInlineeLines.erase(InlineeIter); + InlineeLines->clear(); + delete InlineeLines; + } + LLVM_DEBUG({ + dbgs() << "Merged Inlined lines for: " << Function->getName() << "\n"; + dbgs() << format("Debug lines: %d\n", CULines.size()); + for (const LVLine *Line : CULines) + dbgs() << "Line address: " << hexValue(Line->getOffset()) << ", (" + << Line->getLineNumber() << ")\n"; + ; + }); +} + void LVBinaryReader::print(raw_ostream &OS) const { OS << "LVBinaryReader\n"; LLVM_DEBUG(dbgs() << "PrintReader\n"); Index: llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewReader.cpp @@ -0,0 +1,1233 @@ +//===-- LVCodeViewReader.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 LVCodeViewReader class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" +#include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/PDB/GenericError.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/LinePrinter.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::logicalview; +using namespace llvm::msf; +using namespace llvm::object; +using namespace llvm::pdb; + +#define DEBUG_TYPE "CodeViewReader" + +//===----------------------------------------------------------------------===// +// CodeView Reader. +//===----------------------------------------------------------------------===// +// Use the 'PointerToRawData' as base for the unique offset for the +// Symbol records. Using 'RecordOffset' does not give unique values +// as that offset is relative to each subsection. +uint32_t PointerToRawData = 0; +#define ABSOLUTE_OFFSET(offset) (PointerToRawData + offset) + +std::string LVCodeViewReader::getSymbolKindName(SymbolKind Kind) { + switch (Kind) { +#define SYMBOL_RECORD(EnumName, EnumVal, Name) \ + case EnumName: \ + return #EnumName; +#include "llvm/DebugInfo/CodeView/CodeViewSymbols.def" + default: + return "UnknownSym"; + } +} + +std::string LVCodeViewReader::formatRegisterId(RegisterId Register, + CPUType CPU) { +#define RETURN_CASE(Enum, X, Ret) \ + case Enum::X: \ + return Ret; + + if (CPU == CPUType::ARMNT) { + switch (Register) { +#define CV_REGISTERS_ARM +#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name) +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_ARM + + default: + break; + } + } else if (CPU == CPUType::ARM64) { + switch (Register) { +#define CV_REGISTERS_ARM64 +#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name) +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_ARM64 + + default: + break; + } + } else { + switch (Register) { +#define CV_REGISTERS_X86 +#define CV_REGISTER(name, val) RETURN_CASE(RegisterId, name, #name) +#include "llvm/DebugInfo/CodeView/CodeViewRegisters.def" +#undef CV_REGISTER +#undef CV_REGISTERS_X86 + + default: + break; + } + } + return "formatUnknownEnum(Id)"; +} + +void LVCodeViewReader::printRelocatedField(StringRef Label, + const coff_section *CoffSection, + uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym) { + StringRef SymStorage; + StringRef &Symbol = RelocSym ? *RelocSym : SymStorage; + if (!resolveSymbolName(CoffSection, RelocOffset, Symbol)) + W.printSymbolOffset(Label, Symbol, Offset); + else + W.printHex(Label, RelocOffset); +} + +void LVCodeViewReader::getLinkageName(const coff_section *CoffSection, + uint32_t RelocOffset, uint32_t Offset, + StringRef *RelocSym) { + StringRef SymStorage; + StringRef &Symbol = RelocSym ? *RelocSym : SymStorage; + if (resolveSymbolName(CoffSection, RelocOffset, Symbol)) + *RelocSym = ""; +} + +Expected +LVCodeViewReader::getFileNameForFileOffset(uint32_t FileOffset, + const SymbolGroup *SG) { + if (SG) { + Expected Filename = SG->getNameFromChecksums(FileOffset); + if (!Filename) { + consumeError(Filename.takeError()); + return StringRef(""); + } + return *Filename; + } + + // The file checksum subsection should precede all references to it. + if (!CVFileChecksumTable.valid() || !CVStringTable.valid()) + return createStringError(object_error::parse_failed, getFileName()); + + VarStreamArray::Iterator Iter = + CVFileChecksumTable.getArray().at(FileOffset); + + // Check if the file checksum table offset is valid. + if (Iter == CVFileChecksumTable.end()) + return createStringError(object_error::parse_failed, getFileName()); + + Expected NameOrErr = CVStringTable.getString(Iter->FileNameOffset); + if (!NameOrErr) + return createStringError(object_error::parse_failed, getFileName()); + return *NameOrErr; +} + +Error LVCodeViewReader::printFileNameForOffset(StringRef Label, + uint32_t FileOffset, + const SymbolGroup *SG) { + Expected NameOrErr = getFileNameForFileOffset(FileOffset, SG); + if (!NameOrErr) + return NameOrErr.takeError(); + W.printHex(Label, *NameOrErr, FileOffset); + return Error::success(); +} + +void LVCodeViewReader::cacheRelocations() { + for (const SectionRef &Section : getObj().sections()) { + const coff_section *CoffSection = getObj().getCOFFSection(Section); + + for (const RelocationRef &Relocacion : Section.relocations()) + RelocMap[CoffSection].push_back(Relocacion); + + // Sort relocations by address. + llvm::sort(RelocMap[CoffSection], [](RelocationRef L, RelocationRef R) { + return L.getOffset() < R.getOffset(); + }); + } +} + +// Given a section and an offset into this section the function returns the +// symbol used for the relocation at the offset. +Error LVCodeViewReader::resolveSymbol(const coff_section *CoffSection, + uint64_t Offset, SymbolRef &Sym) { + const auto &Relocations = RelocMap[CoffSection]; + basic_symbol_iterator SymI = getObj().symbol_end(); + for (const RelocationRef &Relocation : Relocations) { + uint64_t RelocationOffset = Relocation.getOffset(); + + if (RelocationOffset == Offset) { + SymI = Relocation.getSymbol(); + break; + } + } + if (SymI == getObj().symbol_end()) + return make_error("Unknown Symbol", inconvertibleErrorCode()); + Sym = *SymI; + return ErrorSuccess(); +} + +// Given a section and an offset into this section the function returns the +// name of the symbol used for the relocation at the offset. +Error LVCodeViewReader::resolveSymbolName(const coff_section *CoffSection, + uint64_t Offset, StringRef &Name) { + SymbolRef Symbol; + if (Error E = resolveSymbol(CoffSection, Offset, Symbol)) + return E; + Expected NameOrErr = Symbol.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + Name = *NameOrErr; + return ErrorSuccess(); +} + +// CodeView and DWARF can have references to compiler generated elements, +// used for initialization. The MSVC includes in the PDBs, internal compile +// units, associated with the MS runtime support. We mark them as 'system' +// and they are printed only if the command line option 'internal=system'. +bool LVCodeViewReader::isSystemEntry(LVElement *Element, StringRef Name) const { + Name = Name.empty() ? Element->getName() : Name; + auto Find = [=](const char *String) -> bool { + return StringRef::npos != Name.find(String); + }; + auto Starts = [=](const char *Pattern) -> bool { + return Name.startswith(Pattern); + }; + auto CheckExclude = [&]() -> bool { + if (Starts("__") || Starts("_PMD") || Starts("_PMFN")) + return true; + if (Find("_s__")) + return true; + if (Find("_CatchableType") || Find("_TypeDescriptor")) + return true; + if (Find("Intermediate\\vctools")) + return true; + if (Find("$initializer$") || Find("dynamic initializer")) + return true; + if (Find("`vftable'") || Find("_GLOBAL__sub")) + return true; + return false; + }; + bool Excluded = CheckExclude(); + if (Excluded) + Element->setIsSystem(); + + return Excluded; +} + +Error LVCodeViewReader::collectInlineeInfo( + DebugInlineeLinesSubsectionRef &Lines, const llvm::pdb::SymbolGroup *SG) { + for (const InlineeSourceLine &Line : Lines) { + TypeIndex TIInlinee = Line.Header->Inlinee; + uint32_t LineNumber = Line.Header->SourceLineNum; + uint32_t FileOffset = Line.Header->FileID; + LLVM_DEBUG({ + DictScope S(W, "InlineeSourceLine"); + LogicalVisitor.printTypeIndex("Inlinee", TIInlinee, StreamTPI); + if (Error Err = printFileNameForOffset("FileID", FileOffset, SG)) + return Err; + W.printNumber("SourceLineNum", LineNumber); + + if (Lines.hasExtraFiles()) { + W.printNumber("ExtraFileCount", Line.ExtraFiles.size()); + ListScope ExtraFiles(W, "ExtraFiles"); + for (const ulittle32_t &FID : Line.ExtraFiles) + if (Error Err = printFileNameForOffset("FileID", FID, SG)) + return Err; + } + }); + Expected NameOrErr = getFileNameForFileOffset(FileOffset, SG); + if (!NameOrErr) + return NameOrErr.takeError(); + LogicalVisitor.addInlineeInfo(TIInlinee, LineNumber, *NameOrErr); + } + + return Error::success(); +} + +Error LVCodeViewReader::traverseInlineeLines(StringRef Subsection) { + BinaryStreamReader SR(Subsection, llvm::support::little); + DebugInlineeLinesSubsectionRef Lines; + if (Error E = Lines.initialize(SR)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + return collectInlineeInfo(Lines); +} + +Error LVCodeViewReader::createLines( + const FixedStreamArray &LineNumbers, LVAddress Addendum, + uint32_t Segment, uint32_t Begin, uint32_t Size, uint32_t NameIndex, + const SymbolGroup *SG) { + LLVM_DEBUG({ + uint32_t End = Begin + Size; + W.getOStream() << formatv("{0:x-4}:{1:x-8}-{2:x-8}\n", Segment, Begin, End); + }); + + for (const LineNumberEntry &Line : LineNumbers) { + if (Line.Offset >= Size) + return createStringError(object_error::parse_failed, getFileName()); + + LineInfo LI(Line.Flags); + + LLVM_DEBUG({ + W.getOStream() << formatv( + "{0} {1:x-8}\n", utostr(LI.getStartLine()), + fmt_align(Begin + Line.Offset, AlignStyle::Right, 8, '0')); + }); + + // The 'processLines()' function will move each created logical line + // to its enclosing logical scope, using the debug ranges information + // and they will be released when its scope parent is deleted. + LVLineDebug *LineDebug = new LVLineDebug(); + CULines.push_back(LineDebug); + LVAddress Address = linearAddress(Segment, Begin + Line.Offset); + LineDebug->setAddress(Address + Addendum); + + if (LI.isAlwaysStepInto()) + LineDebug->setIsAlwaysStepInto(); + else if (LI.isNeverStepInto()) + LineDebug->setIsNeverStepInto(); + else + LineDebug->setLineNumber(LI.getStartLine()); + + if (LI.isStatement()) + LineDebug->setIsNewStatement(); + + Expected NameOrErr = getFileNameForFileOffset(NameIndex, SG); + if (!NameOrErr) + return NameOrErr.takeError(); + LineDebug->setFilename(*NameOrErr); + } + + return Error::success(); +} + +Error LVCodeViewReader::initializeFileAndStringTables( + BinaryStreamReader &Reader) { + while (Reader.bytesRemaining() > 0 && + (!CVFileChecksumTable.valid() || !CVStringTable.valid())) { + // The section consists of a number of subsection in the following format: + // |SubSectionType|SubSectionSize|Contents...| + uint32_t SubType, SubSectionSize; + + if (Error E = Reader.readInteger(SubType)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + if (Error E = Reader.readInteger(SubSectionSize)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + StringRef Contents; + if (Error E = Reader.readFixedString(Contents, SubSectionSize)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + BinaryStreamRef ST(Contents, support::little); + switch (DebugSubsectionKind(SubType)) { + case DebugSubsectionKind::FileChecksums: + if (Error E = CVFileChecksumTable.initialize(ST)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + break; + case DebugSubsectionKind::StringTable: + if (Error E = CVStringTable.initialize(ST)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + break; + default: + break; + } + + uint32_t PaddedSize = alignTo(SubSectionSize, 4); + if (Error E = Reader.skip(PaddedSize - SubSectionSize)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + } + + return Error::success(); +} + +Error LVCodeViewReader::loadTypeServer(TypeServer2Record &TS) { + LLVM_DEBUG({ + W.printString("Guid", formatv("{0}", TS.getGuid()).str()); + W.printNumber("Age", TS.getAge()); + W.printString("Name", TS.getName()); + }); + + SmallString<128> ServerName(TS.getName()); + BuffOrErr = MemoryBuffer::getFile(ServerName); + if (BuffOrErr.getError()) { + // The server name does not exist. Try in the directory as the input file. + ServerName = createAlternativePath(InputFilename, ServerName); + BuffOrErr = MemoryBuffer::getFile(ServerName); + if (BuffOrErr.getError()) { + // For the error message, use the original type server name. + std::string TheFilename(TS.getName()); + return createStringError(errc::bad_file_descriptor, + "File '%s' does not exist.", + TheFilename.c_str()); + } + } + MemBuffer = std::move(BuffOrErr.get()); + + // Check if the buffer corresponds to a PDB file. + assert(identify_magic((*MemBuffer).getBuffer()) == file_magic::pdb && + "Invalid PDB file."); + + if (Error Err = loadDataForPDB(PDB_ReaderType::Native, ServerName, Session)) { + std::string TheFilename(ServerName); + return createStringError(errorToErrorCode(std::move(Err)), "%s", + TheFilename.c_str()); + } + + PdbSession.reset(static_cast(Session.release())); + PDBFile &Pdb = PdbSession->getPDBFile(); + + // Just because a file with a matching name was found and it was an actual + // PDB file doesn't mean it matches. For it to match the InfoStream's GUID + // must match the GUID specified in the TypeServer2 record. + Expected expectedInfo = Pdb.getPDBInfoStream(); + if (expectedInfo && expectedInfo->getGuid() != TS.getGuid()) + return createStringError(errc::invalid_argument, "signature_out_of_date"); + + // The reader needs to switch to a type server, to process the types from + // the server. We need to keep the original input source, as reading other + // sections wil require the input associated with the loaded object file. + TypeServer = std::make_shared(&Pdb); + LogicalVisitor.setInput(TypeServer); + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + if (Error Err = traverseTypes(Pdb, Types, Ids)) + return Err; + + return Error::success(); +} + +Error LVCodeViewReader::loadPrecompiledObject(PrecompRecord &Precomp, + CVTypeArray &CVTypesObj) { + LLVM_DEBUG({ + W.printHex("Count", Precomp.getTypesCount()); + W.printHex("Signature", Precomp.getSignature()); + W.printString("PrecompFile", Precomp.getPrecompFilePath()); + }); + + SmallString<128> ServerName(Precomp.getPrecompFilePath()); + BuffOrErr = MemoryBuffer::getFile(ServerName); + if (BuffOrErr.getError()) { + // The server name does not exist. Try in the directory as the input file. + ServerName = createAlternativePath(InputFilename, ServerName); + if (BuffOrErr.getError()) { + // For the error message, use the original type server name. + std::string TheFilename(Precomp.getPrecompFilePath()); + return createStringError(errc::bad_file_descriptor, + "File '%s' does not exist.", + TheFilename.c_str()); + } + } + MemBuffer = std::move(BuffOrErr.get()); + + Expected> BinOrErr = createBinary(*MemBuffer); + if (errorToErrorCode(BinOrErr.takeError())) { + std::string TheFilename(ServerName); + return createStringError(errc::not_supported, + "Binary object format in '%s' is not supported.", + TheFilename.c_str()); + } + + Binary &BinaryObj = *BinOrErr.get(); + if (!BinaryObj.isCOFF()) { + std::string TheFilename(ServerName); + return createStringError(errc::not_supported, "'%s' is not a COFF object.", + TheFilename.c_str()); + } + + Builder = std::make_unique(BuilderAllocator); + + // The MSVC precompiled header object file, should contain just a single + // ".debug$P" section. + COFFObjectFile &Obj = *cast(&BinaryObj); + for (const SectionRef &Section : Obj.sections()) { + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + return SectionNameOrErr.takeError(); + if (*SectionNameOrErr == ".debug$P") { + Expected DataOrErr = Section.getContents(); + if (!DataOrErr) + return DataOrErr.takeError(); + uint32_t Magic; + if (Error Err = consume(*DataOrErr, Magic)) + return Err; + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return errorCodeToError(object_error::parse_failed); + + ReaderPrecomp = + std::make_unique(*DataOrErr, support::little); + cantFail( + ReaderPrecomp->readArray(CVTypesPrecomp, ReaderPrecomp->getLength())); + + // Append all the type records up to the LF_ENDPRECOMP marker and + // check if the signatures match. + for (const CVType &Type : CVTypesPrecomp) { + ArrayRef TypeData = Type.data(); + if (Type.kind() == LF_ENDPRECOMP) { + EndPrecompRecord EndPrecomp = cantFail( + TypeDeserializer::deserializeAs(TypeData)); + if (Precomp.getSignature() != EndPrecomp.getSignature()) + return createStringError(errc::invalid_argument, "no matching pch"); + break; + } + Builder->insertRecordBytes(TypeData); + } + break; + } + } + + // Append all the type records, skipping the first record which is the + // reference to the precompiled header object information. + for (const CVType &Type : CVTypesObj) { + ArrayRef TypeData = Type.data(); + if (Type.kind() != LF_PRECOMP) + Builder->insertRecordBytes(TypeData); + } + + // Set up a type stream that refers to the added type records. + Builder->ForEachRecord( + [&](TypeIndex TI, const CVType &Type) { TypeArray.push_back(Type); }); + + ItemStream = + std::make_unique>(llvm::support::little); + ItemStream->setItems(TypeArray); + TypeStream.setUnderlyingStream(*ItemStream); + + PrecompHeader = + std::make_shared(TypeStream, TypeArray.size()); + + // Change the original input source to use the collected type records. + LogicalVisitor.setInput(PrecompHeader); + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI, + LogicalVisitor.getShared()); + return visitTypeStream(Types, TDV); +} + +Error LVCodeViewReader::traverseTypeSection(StringRef SectionName, + const SectionRef &Section) { + LLVM_DEBUG({ + ListScope D(W, "CodeViewTypes"); + W.printNumber("Section", SectionName, getObj().getSectionID(Section)); + }); + + Expected DataOrErr = Section.getContents(); + if (!DataOrErr) + return DataOrErr.takeError(); + uint32_t Magic; + if (Error Err = consume(*DataOrErr, Magic)) + return Err; + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return errorCodeToError(object_error::parse_failed); + + // Get the first type record. It will indicate if this object uses a type + // server (/Zi) or a PCH file (/Yu). + CVTypeArray CVTypes; + BinaryStreamReader Reader(*DataOrErr, support::little); + cantFail(Reader.readArray(CVTypes, Reader.getLength())); + CVTypeArray::Iterator FirstType = CVTypes.begin(); + + // The object was compiled with /Zi. It uses types from a type server PDB. + if (FirstType->kind() == LF_TYPESERVER2) { + TypeServer2Record TS = cantFail( + TypeDeserializer::deserializeAs(FirstType->data())); + return loadTypeServer(TS); + } + + // The object was compiled with /Yc or /Yu. It uses types from another + // object file with a matching signature. + if (FirstType->kind() == LF_PRECOMP) { + PrecompRecord Precomp = cantFail( + TypeDeserializer::deserializeAs(FirstType->data())); + return loadPrecompiledObject(Precomp, CVTypes); + } + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + Types.reset(*DataOrErr, 100); + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamTPI, + LogicalVisitor.getShared()); + return visitTypeStream(Types, TDV); +} + +Error LVCodeViewReader::traverseTypes(PDBFile &Pdb, + LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids) { + // Traverse types (TPI and IPI). + auto VisitTypes = [&](LazyRandomTypeCollection &Types, + LazyRandomTypeCollection &Ids, + SpecialStream StreamIdx) -> Error { + LVTypeVisitor TDV(W, &LogicalVisitor, Types, Ids, StreamIdx, + LogicalVisitor.getShared()); + return visitTypeStream(Types, TDV); + }; + + Expected StreamTpiOrErr = Pdb.getPDBTpiStream(); + if (!StreamTpiOrErr) + return StreamTpiOrErr.takeError(); + TpiStream &StreamTpi = *StreamTpiOrErr; + StreamTpi.buildHashMap(); + LLVM_DEBUG({ + W.getOStream() << formatv("Showing {0:N} TPI records\n", + StreamTpi.getNumTypeRecords()); + }); + if (Error Err = VisitTypes(Types, Ids, StreamTPI)) + return Err; + + Expected StreamIpiOrErr = Pdb.getPDBIpiStream(); + if (!StreamIpiOrErr) + return StreamIpiOrErr.takeError(); + TpiStream &StreamIpi = *StreamIpiOrErr; + StreamIpi.buildHashMap(); + LLVM_DEBUG({ + W.getOStream() << formatv("Showing {0:N} IPI records\n", + StreamIpi.getNumTypeRecords()); + }); + return VisitTypes(Ids, Ids, StreamIPI); +} + +Error LVCodeViewReader::traverseSymbolsSubsection(StringRef Subsection, + const SectionRef &Section, + StringRef SectionContents) { + ArrayRef BinaryData(Subsection.bytes_begin(), + Subsection.bytes_end()); + LVSymbolVisitorDelegate VisitorDelegate(this, Section, &getObj(), + SectionContents); + CVSymbolArray Symbols; + BinaryStreamReader Reader(BinaryData, llvm::support::little); + if (Error E = Reader.readArray(Symbols, Reader.getLength())) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(&VisitorDelegate, + CodeViewContainer::ObjectFile); + // As we are processing a COFF format, use TPI as IPI, so the generic code + // to process the CodeView format does not contain any additional checks. + LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, + &VisitorDelegate, LogicalVisitor.getShared()); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Traverser); + CVSymbolVisitor Visitor(Pipeline); + return Visitor.visitSymbolStream(Symbols); +} + +Error LVCodeViewReader::traverseSymbolSection(StringRef SectionName, + const SectionRef &Section) { + LLVM_DEBUG({ + ListScope D(W, "CodeViewDebugInfo"); + W.printNumber("Section", SectionName, getObj().getSectionID(Section)); + }); + + PointerToRawData = getObj().getCOFFSection(Section)->PointerToRawData; + + Expected SectionOrErr = Section.getContents(); + if (!SectionOrErr) + return SectionOrErr.takeError(); + StringRef SectionContents = *SectionOrErr; + StringRef Data = SectionContents; + + SmallVector SymbolNames; + StringMap FunctionLineTables; + + uint32_t Magic; + if (Error E = consume(Data, Magic)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + if (Magic != COFF::DEBUG_SECTION_MAGIC) + return createStringError(object_error::parse_failed, getFileName()); + + BinaryStreamReader FSReader(Data, support::little); + if (Error Err = initializeFileAndStringTables(FSReader)) + return Err; + + while (!Data.empty()) { + // The section consists of a number of subsection in the following format: + // |SubSectionType|SubSectionSize|Contents...| + uint32_t SubType, SubSectionSize; + if (Error E = consume(Data, SubType)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + if (Error E = consume(Data, SubSectionSize)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + if (SubType & SubsectionIgnoreFlag) + SubType &= ~SubsectionIgnoreFlag; + + // Get the contents of the subsection. + if (SubSectionSize > Data.size()) + return createStringError(object_error::parse_failed, getFileName()); + StringRef Contents = Data.substr(0, SubSectionSize); + + // Add SubSectionSize to the current offset and align that offset + // to find the next subsection. + size_t SectionOffset = Data.data() - SectionContents.data(); + size_t NextOffset = SectionOffset + SubSectionSize; + NextOffset = alignTo(NextOffset, 4); + if (NextOffset > SectionContents.size()) + return createStringError(object_error::parse_failed, getFileName()); + Data = SectionContents.drop_front(NextOffset); + + switch (DebugSubsectionKind(SubType)) { + case DebugSubsectionKind::Symbols: + if (Error Err = + traverseSymbolsSubsection(Contents, Section, SectionContents)) + return Err; + break; + + case DebugSubsectionKind::InlineeLines: + if (Error Err = traverseInlineeLines(Contents)) + return Err; + break; + + case DebugSubsectionKind::Lines: + // Holds a PC to file:line table. Some data to parse this subsection + // is stored in the other subsections, so just check sanity and store + // the pointers for deferred processing. + + // Collect function and ranges only if we need to print logical lines. + if (options().getGeneralCollectRanges()) { + + if (SubSectionSize < 12) { + // There should be at least three words to store two function + // relocations and size of the code. + return createStringError(object_error::parse_failed, getFileName()); + } + + StringRef SymbolName; + if (Error Err = resolveSymbolName(getObj().getCOFFSection(Section), + SectionOffset, SymbolName)) + return createStringError(errorToErrorCode(std::move(Err)), + getFileName()); + + LLVM_DEBUG({ W.printString("Symbol Name", SymbolName); }); + if (FunctionLineTables.count(SymbolName) != 0) { + // Saw debug info for this function already? + return createStringError(object_error::parse_failed, getFileName()); + } + + FunctionLineTables[SymbolName] = Contents; + SymbolNames.push_back(SymbolName); + } + break; + + // Do nothing for unrecognized subsections. + default: + break; + } + W.flush(); + } + + // Traverse the line tables now that we've read all the subsections and + // know all the required information. + for (StringRef SymbolName : SymbolNames) { + LLVM_DEBUG({ + ListScope S(W, "FunctionLineTable"); + W.printString("Symbol Name", SymbolName); + }); + + BinaryStreamReader Reader(FunctionLineTables[SymbolName], support::little); + + DebugLinesSubsectionRef Lines; + if (Error E = Lines.initialize(Reader)) + return createStringError(errorToErrorCode(std::move(E)), getFileName()); + + // Find the associate symbol table information. + LVSymbolTableEntry SymbolTableEntry = getSymbolTableEntry(SymbolName); + LVScope *Function = SymbolTableEntry.Scope; + if (!Function) + continue; + + LVAddress Addendum = SymbolTableEntry.Address; + LVSectionIndex SectionIndex = SymbolTableEntry.SectionIndex; + + // The given scope represents the function that contains the line numbers. + // Collect all generated debug lines associated with the function. + CULines.clear(); + + // For the given scope, collect all scopes ranges. + LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); + ScopesWithRanges->clear(); + Function->getRanges(*ScopesWithRanges); + ScopesWithRanges->sort(); + + uint16_t Segment = Lines.header()->RelocSegment; + uint32_t Begin = Lines.header()->RelocOffset; + uint32_t Size = Lines.header()->CodeSize; + for (const LineColumnEntry &Block : Lines) + if (Error Err = createLines(Block.LineNumbers, Addendum, Segment, Begin, + Size, Block.NameIndex)) + return Err; + + // Include lines from any inlined functions within the current function. + includeInlineeLines(SectionIndex, Function); + + if (Error Err = createInstructions(Function, SectionIndex)) + return Err; + + processLines(&CULines, SectionIndex, Function); + } + + return Error::success(); +} + +void LVCodeViewReader::sortScopes() { Root->sort(); } + +void LVCodeViewReader::print(raw_ostream &OS) const { + LLVM_DEBUG(dbgs() << "CreateReaders\n"); +} + +void LVCodeViewReader::mapRangeAddress(const ObjectFile &Obj, + const SectionRef &Section, + bool IsComdat) { + if (!Obj.isCOFF()) + return; + + const COFFObjectFile *Object = cast(&Obj); + + for (const SymbolRef &Sym : Object->symbols()) { + if (!Section.containsSymbol(Sym)) + continue; + + COFFSymbolRef Symbol = Object->getCOFFSymbol(Sym); + if (Symbol.getComplexType() != llvm::COFF::IMAGE_SYM_DTYPE_FUNCTION) + continue; + + StringRef SymbolName; + Expected SymNameOrErr = Object->getSymbolName(Symbol); + if (!SymNameOrErr) { + W.startLine() << "Invalid symbol name: " << Symbol.getSectionNumber() + << "\n"; + consumeError(SymNameOrErr.takeError()); + continue; + } + SymbolName = *SymNameOrErr; + + LLVM_DEBUG({ + Expected SectionOrErr = + Object->getSection(Symbol.getSectionNumber()); + if (!SectionOrErr) { + W.startLine() << "Invalid section number: " << Symbol.getSectionNumber() + << "\n"; + consumeError(SectionOrErr.takeError()); + return; + } + W.printNumber("Section #", Symbol.getSectionNumber()); + W.printString("Name", SymbolName); + W.printHex("Value", Symbol.getValue()); + }); + + // Record the symbol name (linkage) and its loading address. + addToSymbolTable(SymbolName, Symbol.getValue(), Symbol.getSectionNumber(), + IsComdat); + } +} + +Error LVCodeViewReader::createScopes(COFFObjectFile &Obj) { + if (Error Err = loadTargetInfo(Obj)) + return Err; + + // Initialization required when processing a COFF file: + // Cache the symbols relocations. + // Create a mapping for virtual addresses. + // Get the functions entry points. + cacheRelocations(); + mapVirtualAddress(Obj); + + for (const SectionRef &Section : Obj.sections()) { + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + return SectionNameOrErr.takeError(); + // .debug$T is a standard CodeView type section, while .debug$P is the + // same format but used for MSVC precompiled header object files. + if (*SectionNameOrErr == ".debug$T" || *SectionNameOrErr == ".debug$P") + if (Error Err = traverseTypeSection(*SectionNameOrErr, Section)) + return Err; + } + + // Process collected namespaces. + LogicalVisitor.processNamespaces(); + + for (const SectionRef &Section : Obj.sections()) { + Expected SectionNameOrErr = Section.getName(); + if (!SectionNameOrErr) + return SectionNameOrErr.takeError(); + if (*SectionNameOrErr == ".debug$S") + if (Error Err = traverseSymbolSection(*SectionNameOrErr, Section)) + return Err; + } + + // Check if we have to close the Compile Unit scope. + LogicalVisitor.closeScope(); + + // Traverse the strings recorded and transform them into filenames. + LogicalVisitor.processFiles(); + + // Process collected element lines. + LogicalVisitor.processLines(); + + // Translate composite names into a single component. + Root->transformScopedName(); + return Error::success(); +} + +Error LVCodeViewReader::createScopes(PDBFile &Pdb) { + if (Error Err = loadTargetInfo(Pdb)) + return Err; + + if (!Pdb.hasPDBTpiStream() || !Pdb.hasPDBDbiStream()) + return Error::success(); + + // Open the executable associated with the PDB file and get the section + // addresses used to calculate linear addresses for CodeView Symbols. + if (!ExePath.empty()) { + ErrorOr> BuffOrErr = + MemoryBuffer::getFileOrSTDIN(ExePath); + if (BuffOrErr.getError()) { + std::string TheFilename(ExePath); + return createStringError(errc::bad_file_descriptor, + "File '%s' does not exist.", + TheFilename.c_str()); + } + BinaryBuffer = std::move(BuffOrErr.get()); + + // Check if the buffer corresponds to a PECOFF executable. + assert(identify_magic(BinaryBuffer->getBuffer()) == + file_magic::pecoff_executable && + "Invalid PECOFF executable file."); + + Expected> BinOrErr = + createBinary(BinaryBuffer->getMemBufferRef()); + if (errorToErrorCode(BinOrErr.takeError())) { + std::string TheFilename(ExePath); + return createStringError(errc::not_supported, + "Binary object format in '%s' is not supported.", + TheFilename.c_str()); + } + BinaryExecutable = std::move(*BinOrErr); + if (COFFObjectFile *COFFObject = + dyn_cast(BinaryExecutable.get())) + mapVirtualAddress(*COFFObject); + } + + // In order to generate a full logical view, we have to traverse both + // streams TPI and IPI if they are present. The following table gives + // the stream where a specified type is located. If the IPI stream is + // not present, all the types are located in the TPI stream. + // + // TPI Stream: + // LF_POINTER LF_MODIFIER LF_PROCEDURE LF_MFUNCTION + // LF_LABEL LF_ARGLIST LF_FIELDLIST LF_ARRAY + // LF_CLASS LF_STRUCTURE LF_INTERFACE LF_UNION + // LF_ENUM LF_TYPESERVER2 LF_VFTABLE LF_VTSHAPE + // LF_BITFIELD LF_METHODLIST LF_PRECOMP LF_ENDPRECOMP + // + // IPI stream: + // LF_FUNC_ID LF_MFUNC_ID LF_BUILDINFO + // LF_SUBSTR_LIST LF_STRING_ID LF_UDT_SRC_LINE + // LF_UDT_MOD_SRC_LINE + + LazyRandomTypeCollection &Types = types(); + LazyRandomTypeCollection &Ids = ids(); + if (Error Err = traverseTypes(Pdb, Types, Ids)) + return Err; + + // Process collected namespaces. + LogicalVisitor.processNamespaces(); + + LLVM_DEBUG({ W.getOStream() << "Traversing inlined lines\n"; }); + + auto VisitInlineeLines = [&](int32_t Modi, const SymbolGroup &SG, + DebugInlineeLinesSubsectionRef &Lines) -> Error { + return collectInlineeInfo(Lines, &SG); + }; + + FilterOptions Filters = {}; + LinePrinter Printer(/*Indent=*/2, false, nulls(), Filters); + const PrintScope HeaderScope(Printer, /*IndentLevel=*/2); + if (Error Err = iterateModuleSubsections( + Input, HeaderScope, VisitInlineeLines)) + return Err; + + // Traverse global symbols. + LLVM_DEBUG({ W.getOStream() << "Traversing global symbols\n"; }); + if (Pdb.hasPDBGlobalsStream()) { + Expected GlobalsOrErr = Pdb.getPDBGlobalsStream(); + if (!GlobalsOrErr) + return GlobalsOrErr.takeError(); + GlobalsStream &Globals = *GlobalsOrErr; + const GSIHashTable &Table = Globals.getGlobalsTable(); + Expected ExpectedSyms = Pdb.getPDBSymbolStream(); + if (ExpectedSyms) { + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr, + LogicalVisitor.getShared()); + + // As the global symbols do not have an associated Compile Unit, create + // one, as the container for all global symbols. + RecordPrefix Prefix(SymbolKind::S_COMPILE3); + CVSymbol Symbol(&Prefix, sizeof(Prefix)); + uint32_t Offset = 0; + if (Error Err = Traverser.visitSymbolBegin(Symbol, Offset)) + consumeError(std::move(Err)); + else { + // The CodeView compile unit containing the global symbols does not + // have a name; use the and it is marked as such. Just append the + // '_global' string. + std::string Name(CompileUnit->getParentScope()->getName()); + CompileUnit->setName(Name.append("_global")); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Traverser); + CVSymbolVisitor Visitor(Pipeline); + + BinaryStreamRef SymStream = + ExpectedSyms->getSymbolArray().getUnderlyingStream(); + for (uint32_t PubSymOff : Table) { + Expected Sym = readSymbolFromStream(SymStream, PubSymOff); + if (Sym) { + if (Error Err = Visitor.visitSymbolRecord(*Sym, PubSymOff)) + return createStringError(errorToErrorCode(std::move(Err)), + getFileName()); + } else + consumeError(Sym.takeError()); + } + } + + LogicalVisitor.closeScope(); + } else + consumeError(ExpectedSyms.takeError()); + } + + // Traverse symbols (DBI). + LLVM_DEBUG({ W.getOStream() << "Traversing symbol groups\n"; }); + + auto VisitSymbolGroup = [&](uint32_t Modi, const SymbolGroup &SG) -> Error { + Expected ExpectedModS = + getModuleDebugStream(Pdb, Modi); + if (ExpectedModS) { + ModuleDebugStreamRef &ModS = *ExpectedModS; + + LLVM_DEBUG({ + W.getOStream() << formatv("Traversing Group: Mod {0:4}\n", Modi); + }); + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + LVSymbolVisitor Traverser(this, W, &LogicalVisitor, Types, Ids, nullptr, + LogicalVisitor.getShared()); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Traverser); + CVSymbolVisitor Visitor(Pipeline); + BinarySubstreamRef SS = ModS.getSymbolsSubstream(); + if (Error Err = + Visitor.visitSymbolStream(ModS.getSymbolArray(), SS.Offset)) + return createStringError(errorToErrorCode(std::move(Err)), + getFileName()); + } else { + // If the module stream does not exist, it is not an error condition. + consumeError(ExpectedModS.takeError()); + } + + return Error::success(); + }; + + if (Error Err = iterateSymbolGroups(Input, HeaderScope, VisitSymbolGroup)) + return Err; + + // At this stage, the logical view contains all scopes, symbols and types. + // For PDBs we can use the module id, to access its specific compile unit. + // The line record addresses has been already resolved, so we can apply the + // flow as when processing DWARF. + + LLVM_DEBUG({ W.getOStream() << "Traversing lines\n"; }); + + // Record all line records for a Compile Unit. + CULines.clear(); + + auto VisitDebugLines = [this](int32_t Modi, const SymbolGroup &SG, + DebugLinesSubsectionRef &Lines) -> Error { + if (!options().getPrintLines()) + return Error::success(); + + uint16_t Segment = Lines.header()->RelocSegment; + uint32_t Begin = Lines.header()->RelocOffset; + uint32_t Size = Lines.header()->CodeSize; + + LLVM_DEBUG({ W.getOStream() << formatv("Modi = {0}\n", Modi); }); + + // We have line information for a new module; finish processing the + // collected information for the current module. Once it is done, start + // recording the line information for the new module. + if (CurrentModule != Modi) { + if (Error Err = processModule()) + return Err; + CULines.clear(); + CurrentModule = Modi; + } + + for (const LineColumnEntry &Block : Lines) + if (Error Err = createLines(Block.LineNumbers, /*Addendum=*/0, Segment, + Begin, Size, Block.NameIndex, &SG)) + return Err; + + return Error::success(); + }; + + if (Error Err = iterateModuleSubsections( + Input, HeaderScope, VisitDebugLines)) + return Err; + + // Check if we have to close the Compile Unit scope. + LogicalVisitor.closeScope(); + + // Process collected element lines. + LogicalVisitor.processLines(); + + // Translate composite names into a single component. + Root->transformScopedName(); + return Error::success(); +} + +Error LVCodeViewReader::processModule() { + if (LVScope *Scope = getScopeForModule(CurrentModule)) { + CompileUnit = static_cast(Scope); + + LLVM_DEBUG({ dbgs() << "Processing Scope: " << Scope->getName() << "\n"; }); + + // For the given compile unit, collect all scopes ranges. + // For a complete ranges and lines mapping, the logical view support + // needs for the compile unit to have a low and high pc values. We + // can traverse the 'Modules' section and get the information for the + // specific module. Another option, is from all the ranges collected + // to take the first and last values. + LVSectionIndex SectionIndex = DotTextSectionIndex; + LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); + ScopesWithRanges->clear(); + CompileUnit->getRanges(*ScopesWithRanges); + if (!ScopesWithRanges->empty()) + CompileUnit->addObject(ScopesWithRanges->getLower(), + ScopesWithRanges->getUpper()); + ScopesWithRanges->sort(); + + if (Error Err = createInstructions()) + return Err; + + // Include lines from any inlined functions within the current function. + includeInlineeLines(SectionIndex, Scope); + + processLines(&CULines, SectionIndex, nullptr); + } + + return Error::success(); +} + +// In order to create the scopes, the CodeView Reader: +// = Traverse the TPI/IPI stream (Type visitor): +// Collect forward references, scoped names, type indexes that will represent +// a logical element, strings, line records, linkage names. +// = Traverse the symbols section (Symbol visitor): +// It creates the scopes tree and creates the required logical elements, by +// using the collected indexes from the type visitor. +Error LVCodeViewReader::createScopes() { + LLVM_DEBUG({ + W.startLine() << "\n"; + W.printString("File", getFileName().str()); + W.printString("Exe", ExePath); + W.printString("Format", FileFormatName); + }); + + if (Error Err = LVReader::createScopes()) + return Err; + + LogicalVisitor.setRoot(Root); + + if (isObj()) { + if (Error Err = createScopes(getObj())) + return Err; + } else { + if (Error Err = createScopes(getPdb())) + return Err; + } + + return Error::success(); +} + +Error LVCodeViewReader::loadTargetInfo(const ObjectFile &Obj) { + // Detect the architecture from the object file. We usually don't need OS + // info to lookup a target and create register info. + Triple TT; + TT.setArch(Triple::ArchType(Obj.getArch())); + TT.setVendor(Triple::UnknownVendor); + TT.setOS(Triple::UnknownOS); + + // Features to be passed to target/subtarget + SubtargetFeatures Features = Obj.getFeatures(); + + return loadGenericTargetInfo(TT.str(), Features.getString()); +} + +Error LVCodeViewReader::loadTargetInfo(const PDBFile &Pdb) { + Triple TT; + TT.setArch(Triple::ArchType::x86_64); + TT.setVendor(Triple::UnknownVendor); + TT.setOS(Triple::Win32); + + StringRef TheFeature = ""; + + return loadGenericTargetInfo(TT.str(), TheFeature); +} + +std::string LVCodeViewReader::getRegisterName(LVSmall Opcode, + uint64_t Operands[2]) { + // Get Compilation Unit CPU Type. + CPUType CPU = getCompileUnitCPUType(); + // For CodeView the register always is in Operands[0]; + RegisterId Register = (RegisterId(Operands[0])); + return formatRegisterId(Register, CPU); +} Index: llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp =================================================================== --- /dev/null +++ llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp @@ -0,0 +1,3530 @@ +//===-- LVCodeViewVisitor.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 LVCodeViewVisitor class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/DebugInfo/CodeView/EnumTables.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/InputFile.h" +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h" +#include "llvm/DebugInfo/PDB/Native/RawError.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatAdapters.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::codeview; +using namespace llvm::object; +using namespace llvm::pdb; +using namespace llvm::logicalview; + +#define DEBUG_TYPE "CodeViewUtilities" + +namespace llvm { +namespace logicalview { + +static TypeIndex getTrueType(TypeIndex &TI) { + // Dealing with a MSVC generated PDB, we encountered a type index with the + // value of: 0x0280xxxx where xxxx=0000. + // + // There is some documentation about type indices: + // https://llvm.org/docs/PDB/TpiStream.html + // + // A type index is a 32-bit integer that uniquely identifies a type inside + // of an object file’s .debug$T section or a PDB file’s TPI or IPI stream. + // The value of the type index for the first type record from the TPI stream + // is given by the TypeIndexBegin member of the TPI Stream Header although + // in practice this value is always equal to 0x1000 (4096). + // + // Any type index with a high bit set is considered to come from the IPI + // stream, although this appears to be more of a hack, and LLVM does not + // generate type indices of this nature. They can, however, be observed in + // Microsoft PDBs occasionally, so one should be prepared to handle them. + // Note that having the high bit set is not a necessary condition to + // determine whether a type index comes from the IPI stream, it is only + // sufficient. + LLVM_DEBUG( + { dbgs() << "Index before: " << HexNumber(TI.getIndex()) << "\n"; }); + TI.setIndex(TI.getIndex() & 0x0000ffff); + LLVM_DEBUG( + { dbgs() << "Index after: " << HexNumber(TI.getIndex()) << "\n"; }); + return TI; +} + +static const EnumEntry LeafTypeNames[] = { +#define CV_TYPE(enum, val) {#enum, enum}, +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" +}; + +// Return the type name pointed by the type index. It uses the kind to query +// the associated name for the record type. +static StringRef getRecordName(LazyRandomTypeCollection &Types, TypeIndex TI) { + if (TI.isSimple()) + return {}; + + StringRef RecordName; + CVType CVReference = Types.getType(TI); + auto GetName = [&](auto Record) { + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(CVReference), Record)) + consumeError(std::move(Err)); + else + RecordName = Record.getName(); + }; + + TypeRecordKind RK = static_cast(CVReference.kind()); + if (RK == TypeRecordKind::Class || RK == TypeRecordKind::Struct) + GetName(ClassRecord(RK)); + else if (RK == TypeRecordKind::Union) + GetName(UnionRecord(RK)); + else if (RK == TypeRecordKind::Enum) + GetName(EnumRecord(RK)); + + return RecordName; +} + +} // namespace logicalview +} // namespace llvm + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewDataVisitor" + +namespace llvm { +namespace logicalview { + +// Keeps the type indexes with line information. +using LVLineRecords = std::vector; + +namespace { + +class LVTypeRecords { + LVShared *Shared = nullptr; + + // Logical elements associated to their CodeView Type Index. + using RecordEntry = std::pair; + using RecordTable = std::map; + RecordTable RecordFromTypes; + RecordTable RecordFromIds; + + using NameTable = std::map; + NameTable NameFromTypes; + NameTable NameFromIds; + +public: + LVTypeRecords(LVShared *Shared) : Shared(Shared){}; + + void add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind, + LVElement *Element = nullptr); + void add(uint32_t StreamIdx, TypeIndex TI, StringRef Name); + LVElement *find(uint32_t StreamIdx, TypeIndex TI); + TypeIndex find(uint32_t StreamIdx, StringRef Name); +}; + +class LVForwardReferences { + // Forward reference and its definitions (Name as key). + using ForwardEntry = std::pair; + using ForwardTypeNames = std::map; + ForwardTypeNames ForwardTypesNames; + + // Forward reference and its definition (TypeIndex as key). + using ForwardType = std::map; + ForwardType ForwardTypes; + + // Forward types and its references. + void add(TypeIndex TIForward, TypeIndex TIReference) { + ForwardTypes.emplace(TIForward, TIReference); + } + + void add(StringRef Name, TypeIndex TIForward) { + if (ForwardTypesNames.find(Name) == ForwardTypesNames.end()) { + ForwardTypesNames.emplace( + std::piecewise_construct, std::forward_as_tuple(Name), + std::forward_as_tuple(TIForward, TypeIndex::None())); + } else { + // Update a recorded definition with its reference. + ForwardTypesNames[Name].first = TIForward; + add(TIForward, ForwardTypesNames[Name].second); + } + } + + // Update a previously recorded forward reference with its definition. + void update(StringRef Name, TypeIndex TIReference) { + if (ForwardTypesNames.find(Name) != ForwardTypesNames.end()) { + // Update the recorded forward reference with its definition. + ForwardTypesNames[Name].second = TIReference; + add(ForwardTypesNames[Name].first, TIReference); + } else { + // We have not seen the forward reference. Insert the definition. + ForwardTypesNames.emplace( + std::piecewise_construct, std::forward_as_tuple(Name), + std::forward_as_tuple(TypeIndex::None(), TIReference)); + } + } + +public: + LVForwardReferences() = default; + + void record(bool IsForwardRef, StringRef Name, TypeIndex TI) { + // We are expecting for the forward references to be first. But that + // is not always the case. A name must be recorded regardless of the + // order in which the forward reference appears. + (IsForwardRef) ? add(Name, TI) : update(Name, TI); + } + + TypeIndex find(TypeIndex TIForward) { + return (ForwardTypes.find(TIForward) != ForwardTypes.end()) + ? ForwardTypes[TIForward] + : TypeIndex::None(); + } + + TypeIndex find(StringRef Name) { + return (ForwardTypesNames.find(Name) != ForwardTypesNames.end()) + ? ForwardTypesNames[Name].second + : TypeIndex::None(); + } + + // If the given TI corresponds to a reference, return the reference. + // Otherwise return the given TI. + TypeIndex remap(TypeIndex TI) { + TypeIndex Forward = find(TI); + return Forward.isNoneType() ? TI : Forward; + } +}; + +// Namespace deduction. +class LVNamespaceDeduction { + LVShared *Shared = nullptr; + + using Names = std::map; + Names NamespaceNames; + + using LookupSet = std::set; + LookupSet DeductedScopes; + LookupSet UnresolvedScopes; + LookupSet IdentifiedNamespaces; + + void add(StringRef Name, LVScope *Namespace) { + if (NamespaceNames.find(Name) == NamespaceNames.end()) + NamespaceNames.emplace(Name, Namespace); + } + +public: + LVNamespaceDeduction(LVShared *Shared) : Shared(Shared){}; + + void init(); + void add(StringRef String); + LVScope *get(LVStringRefs Components); + LVScope *get(StringRef Name, bool CheckScope = true); + + // Find the logical namespace for the 'Name' component. + LVScope *find(StringRef Name) { + LVScope *Namespace = (NamespaceNames.find(Name) != NamespaceNames.end()) + ? NamespaceNames[Name] + : nullptr; + return Namespace; + } + + // For the given lexical components, return a tuple with the first entry + // being the outermost namespace and the second entry being the first + // non-namespace. + LVLexicalIndex find(LVStringRefs Components) { + if (Components.empty()) + return {}; + + LVStringRefs::size_type FirstNamespace = 0; + LVStringRefs::size_type FirstNonNamespace = Components.size() - 1; + for (LVStringRefs::size_type Index = 0; Index < Components.size(); + ++Index) { + FirstNonNamespace = Index; + LookupSet::iterator Iter = IdentifiedNamespaces.find(Components[Index]); + if (Iter == IdentifiedNamespaces.end()) + // The component is not a namespace name. + break; + } + return std::make_tuple(FirstNamespace, FirstNonNamespace); + } +}; + +// Strings. +class LVStringRecords { + using StringEntry = std::tuple; + using StringIds = std::map; + StringIds Strings; + +public: + LVStringRecords() = default; + + void add(TypeIndex TI, StringRef String) { + static uint32_t Index = 0; + if (Strings.find(TI) == Strings.end()) + Strings.emplace( + std::piecewise_construct, std::forward_as_tuple(TI), + std::forward_as_tuple(++Index, std::string(String), nullptr)); + } + + StringRef find(TypeIndex TI) { + StringIds::iterator Iter = Strings.find(TI); + return Iter != Strings.end() ? std::get<1>(Iter->second) : StringRef{}; + } + + uint32_t findIndex(TypeIndex TI) { + StringIds::iterator Iter = Strings.find(TI); + return Iter != Strings.end() ? std::get<0>(Iter->second) : 0; + } + + // Move strings representing the filenames to the compile unit. + void addFilenames(); + void addFilenames(LVScopeCompileUnit *Scope); +}; +} // namespace + +using LVTypeKinds = std::set; +using LVSymbolKinds = std::set; + +// The following data keeps forward information, type records, names for +// namespace deduction, strings records, line records. +// It is shared by the type visitor, symbol visitor and logical visitor and +// it is independent from the CodeViewReader. +struct LVShared { + LVCodeViewReader *Reader = nullptr; + LVLogicalVisitor *Visitor = nullptr; + LVForwardReferences ForwardReferences; + LVLineRecords LineRecords; + LVNamespaceDeduction NamespaceDeduction; + LVStringRecords StringRecords; + LVTypeRecords TypeRecords; + + // In order to determine which types and/or symbols records should be handled + // by the reader, we record record kinds seen by the type and symbol visitors. + // At the end of the scopes creation, the '--internal=tag' option will allow + // to print the unique record ids collected. + LVTypeKinds TypeKinds; + LVSymbolKinds SymbolKinds; + + LVShared(LVCodeViewReader *Reader, LVLogicalVisitor *Visitor) + : Reader(Reader), Visitor(Visitor), NamespaceDeduction(this), + TypeRecords(this) {} + ~LVShared() = default; +}; +} // namespace logicalview +} // namespace llvm + +void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, TypeLeafKind Kind, + LVElement *Element) { + RecordTable &Target = + (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds; + Target.emplace(std::piecewise_construct, std::forward_as_tuple(TI), + std::forward_as_tuple(Kind, Element)); +} + +void LVTypeRecords::add(uint32_t StreamIdx, TypeIndex TI, StringRef Name) { + NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds; + Target.emplace(Name, TI); +} + +LVElement *LVTypeRecords::find(uint32_t StreamIdx, TypeIndex TI) { + RecordTable &Target = + (StreamIdx == StreamTPI) ? RecordFromTypes : RecordFromIds; + + LVElement *Element = nullptr; + RecordTable::iterator Iter = Target.find(TI); + if (Iter != Target.end()) { + Element = Iter->second.second; + if (Element) + return Element; + + // Create the logical element if not found. + Element = Shared->Visitor->createElement(Iter->second.first); + if (Element) { + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + Target[TI].second = Element; + } + } + return Element; +} + +TypeIndex LVTypeRecords::find(uint32_t StreamIdx, StringRef Name) { + NameTable &Target = (StreamIdx == StreamTPI) ? NameFromTypes : NameFromIds; + NameTable::iterator Iter = Target.find(Name); + return Iter != Target.end() ? Iter->second : TypeIndex::None(); +} + +void LVStringRecords::addFilenames() { + for (StringIds::const_reference Entry : Strings) { + StringRef Name = std::get<1>(Entry.second); + LVScopeCompileUnit *Scope = std::get<2>(Entry.second); + Scope->addFilename(transformPath(Name)); + } + Strings.clear(); +} + +void LVStringRecords::addFilenames(LVScopeCompileUnit *Scope) { + for (StringIds::reference Entry : Strings) + if (!std::get<2>(Entry.second)) + std::get<2>(Entry.second) = Scope; +} + +void LVNamespaceDeduction::add(StringRef String) { + StringRef InnerComponent; + StringRef OuterComponent; + std::tie(OuterComponent, InnerComponent) = getInnerComponent(String); + DeductedScopes.insert(InnerComponent); + if (OuterComponent.size()) + UnresolvedScopes.insert(OuterComponent); +} + +void LVNamespaceDeduction::init() { + // We have 2 sets of names: + // - deducted scopes (class, structure, union and enum) and + // - unresolved scopes, that can represent namespaces or any deducted. + // Before creating the namespaces, we have to traverse the unresolved + // and remove any references to already deducted scopes. + LVStringRefs Components; + for (const StringRef &Unresolved : UnresolvedScopes) { + Components = getAllLexicalComponents(Unresolved); + for (const StringRef &Component : Components) { + LookupSet::iterator Iter = DeductedScopes.find(Component); + if (Iter == DeductedScopes.end()) + IdentifiedNamespaces.insert(Component); + } + } + + LLVM_DEBUG({ + auto Print = [&](LookupSet &Container, const char *Title) { + auto Header = [&]() { + dbgs() << formatv("\n{0}\n", fmt_repeat('=', 72)); + dbgs() << formatv("{0}\n", Title); + dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); + }; + Header(); + for (const StringRef &Item : Container) { + std::string TheItem(Item); + dbgs() << formatv("'{0}'\n", TheItem.c_str()); + } + }; + + Print(DeductedScopes, "Deducted Scopes"); + Print(UnresolvedScopes, "Unresolved Scopes"); + Print(IdentifiedNamespaces, "Namespaces"); + }); +} + +LVScope *LVNamespaceDeduction::get(LVStringRefs Components) { + LLVM_DEBUG({ + for (const StringRef &Component : Components) { + std::string TheComponent(Component); + dbgs() << formatv("'{0}'\n", TheComponent.c_str()); + } + }); + + if (Components.empty()) + return nullptr; + + // Update the namespaces relationship. + LVScope *Namespace = nullptr; + LVScope *Parent = Shared->Reader->getCompileUnit(); + for (const StringRef &Component : Components) { + // Check if we have seen the namespace. + Namespace = find(Component); + if (!Namespace) { + // We have identified namespaces that are generated by MSVC. Mark them + // as 'system' so they will be excluded from the logical view. + Namespace = new LVScopeNamespace(); + Namespace->setTag(dwarf::DW_TAG_namespace); + Namespace->setName(Component); + Parent->addElement(Namespace); + getReader().isSystemEntry(Namespace); + add(Component, Namespace); + } + Parent = Namespace; + } + return Parent; +} + +LVScope *LVNamespaceDeduction::get(StringRef ScopedName, bool CheckScope) { + LVStringRefs Components = getAllLexicalComponents(ScopedName); + if (CheckScope) + Components.erase(std::remove_if(Components.begin(), Components.end(), + [&](StringRef Component) { + LookupSet::iterator Iter = + IdentifiedNamespaces.find(Component); + return Iter == IdentifiedNamespaces.end(); + }), + Components.end()); + + LLVM_DEBUG({ + std::string TheScopedName(ScopedName); + dbgs() << formatv("ScopedName: '{0}'\n", TheScopedName.c_str()); + }); + + return get(Components); +} + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewTypeVisitor" + +//===----------------------------------------------------------------------===// +// TypeRecord traversal. +//===----------------------------------------------------------------------===// +void LVTypeVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI, + uint32_t StreamIdx) const { + codeview::printTypeIndex(W, FieldName, TI, + StreamIdx == StreamTPI ? Types : Ids); +} + +Error LVTypeVisitor::visitTypeBegin(CVType &Record) { + return visitTypeBegin(Record, TypeIndex::fromArrayIndex(Types.size())); +} + +Error LVTypeVisitor::visitTypeBegin(CVType &Record, TypeIndex TI) { + LLVM_DEBUG({ + W.getOStream() << formatTypeLeafKind(Record.kind()); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")\n"; + }); + + if (options().getInternalTag()) + Shared->TypeKinds.insert(Record.kind()); + + // The collected type records, will be use to create the logical elements + // during the symbols traversal when a type is referenced. + CurrentTypeIndex = TI; + Shared->TypeRecords.add(StreamIdx, TI, Record.kind()); + return Error::success(); +} + +Error LVTypeVisitor::visitUnknownType(CVType &Record) { + LLVM_DEBUG({ W.printNumber("Length", uint32_t(Record.content().size())); }); + return Error::success(); +} + +Error LVTypeVisitor::visitMemberBegin(CVMemberRecord &Record) { + LLVM_DEBUG({ + W.startLine() << formatTypeLeafKind(Record.Kind); + W.getOStream() << " {\n"; + W.indent(); + }); + return Error::success(); +} + +Error LVTypeVisitor::visitMemberEnd(CVMemberRecord &Record) { + LLVM_DEBUG({ + W.unindent(); + W.startLine() << "}\n"; + }); + return Error::success(); +} + +Error LVTypeVisitor::visitUnknownMember(CVMemberRecord &Record) { + LLVM_DEBUG({ W.printHex("UnknownMember", unsigned(Record.Kind)); }); + return Error::success(); +} + +// LF_BUILDINFO (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, BuildInfoRecord &Args) { + // All the args are references into the TPI/IPI stream. + LLVM_DEBUG({ + W.printNumber("NumArgs", static_cast(Args.getArgs().size())); + ListScope Arguments(W, "Arguments"); + for (TypeIndex Arg : Args.getArgs()) + printTypeIndex("ArgType", Arg, StreamIPI); + }); + + // Only add the strings that hold information about filenames. They will be + // used to complete the line/file information for the logical elements. + // There are other strings holding information about namespaces. + TypeIndex TI; + StringRef String; + + // Absolute CWD path + TI = Args.getArgs()[BuildInfoRecord::BuildInfoArg::CurrentDirectory]; + String = Ids.getTypeName(TI); + if (!String.empty()) + Shared->StringRecords.add(TI, String); + + // Get the compile unit name. + TI = Args.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile]; + String = Ids.getTypeName(TI); + if (!String.empty()) + Shared->StringRecords.add(TI, String); + LogicalVisitor->setCompileUnitName(std::string(String)); + + return Error::success(); +} + +// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, ClassRecord &Class) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("FieldListType", Class.getFieldList(), StreamTPI); + W.printString("Name", Class.getName()); + }); + + // Collect class name for scope deduction. + Shared->NamespaceDeduction.add(Class.getName()); + Shared->ForwardReferences.record(Class.isForwardRef(), Class.getName(), + CurrentTypeIndex); + + // Collect class name for contained scopes deduction. + Shared->TypeRecords.add(StreamIdx, CurrentTypeIndex, Class.getName()); + return Error::success(); +} + +// LF_ENUM (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, EnumRecord &Enum) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("FieldListType", Enum.getFieldList(), StreamTPI); + W.printString("Name", Enum.getName()); + }); + + // Collect enum name for scope deduction. + Shared->NamespaceDeduction.add(Enum.getName()); + return Error::success(); +} + +// LF_FUNC_ID (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, FuncIdRecord &Func) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("Type", Func.getFunctionType(), StreamTPI); + printTypeIndex("Parent", Func.getParentScope(), StreamTPI); + W.printString("Name", Func.getName()); + }); + + // Collect function name for scope deduction. + Shared->NamespaceDeduction.add(Func.getName()); + return Error::success(); +} + +// LF_PROCEDURE (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, ProcedureRecord &Proc) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", CurrentTypeIndex, StreamTPI); + printTypeIndex("ReturnType", Proc.getReturnType(), StreamTPI); + W.printNumber("NumParameters", Proc.getParameterCount()); + printTypeIndex("ArgListType", Proc.getArgumentList(), StreamTPI); + }); + + // Collect procedure information as they can be referenced by typedefs. + Shared->TypeRecords.add(StreamTPI, CurrentTypeIndex, {}); + return Error::success(); +} + +// LF_STRING_ID (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, StringIdRecord &String) { + // No additional references are needed. + LLVM_DEBUG({ + printTypeIndex("Id", String.getId(), StreamIPI); + W.printString("StringData", String.getString()); + }); + return Error::success(); +} + +// LF_UDT_SRC_LINE (TPI)/(IPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, + UdtSourceLineRecord &Line) { + // UDT and SourceFile are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("UDT", Line.getUDT(), StreamIPI); + printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", Line.getLineNumber()); + }); + + Shared->LineRecords.push_back(CurrentTypeIndex); + return Error::success(); +} + +// LF_UNION (TPI) +Error LVTypeVisitor::visitKnownRecord(CVType &Record, UnionRecord &Union) { + LLVM_DEBUG({ + W.printNumber("MemberCount", Union.getMemberCount()); + printTypeIndex("FieldList", Union.getFieldList(), StreamTPI); + W.printNumber("SizeOf", Union.getSize()); + W.printString("Name", Union.getName()); + if (Union.hasUniqueName()) + W.printString("UniqueName", Union.getUniqueName()); + }); + + // Collect union name for scope deduction. + Shared->NamespaceDeduction.add(Union.getName()); + Shared->ForwardReferences.record(Union.isForwardRef(), Union.getName(), + CurrentTypeIndex); + + // Collect class name for contained scopes deduction. + Shared->TypeRecords.add(StreamIdx, CurrentTypeIndex, Union.getName()); + return Error::success(); +} + +namespace llvm { +namespace logicalview { + +// Current elements during the processing of a RecordType or RecordSymbol. +LVElement *CurrentElement = nullptr; +LVScope *CurrentScope = nullptr; +LVSymbol *CurrentSymbol = nullptr; +LVType *CurrentType = nullptr; + +} // namespace logicalview +} // namespace llvm + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewSymbolVisitor" + +//===----------------------------------------------------------------------===// +// SymbolRecord traversal. +//===----------------------------------------------------------------------===// +void LVSymbolVisitorDelegate::printRelocatedField(StringRef Label, + uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym) { + Reader->printRelocatedField(Label, CoffSection, RelocOffset, Offset, + RelocSym); +} + +void LVSymbolVisitorDelegate::getLinkageName(uint32_t RelocOffset, + uint32_t Offset, + StringRef *RelocSym) { + Reader->getLinkageName(CoffSection, RelocOffset, Offset, RelocSym); +} + +StringRef +LVSymbolVisitorDelegate::getFileNameForFileOffset(uint32_t FileOffset) { + Expected Name = Reader->getFileNameForFileOffset(FileOffset); + if (!Name) { + consumeError(Name.takeError()); + return {}; + } + return *Name; +} + +DebugStringTableSubsectionRef LVSymbolVisitorDelegate::getStringTable() { + return Reader->CVStringTable; +} + +void LVSymbolVisitor::printLocalVariableAddrRange( + const LocalVariableAddrRange &Range, uint32_t RelocationOffset) { + DictScope S(W, "LocalVariableAddrRange"); + if (ObjDelegate) + ObjDelegate->printRelocatedField("OffsetStart", RelocationOffset, + Range.OffsetStart); + W.printHex("ISectStart", Range.ISectStart); + W.printHex("Range", Range.Range); +} + +void LVSymbolVisitor::printLocalVariableAddrGap( + ArrayRef Gaps) { + for (const LocalVariableAddrGap &Gap : Gaps) { + ListScope S(W, "LocalVariableAddrGap"); + W.printHex("GapStartOffset", Gap.GapStartOffset); + W.printHex("Range", Gap.Range); + } +} + +void LVSymbolVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI) const { + codeview::printTypeIndex(W, FieldName, TI, Types); +} + +Error LVSymbolVisitor::visitSymbolBegin(CVSymbol &Record) { + return visitSymbolBegin(Record, 0); +} + +Error LVSymbolVisitor::visitSymbolBegin(CVSymbol &Record, uint32_t Offset) { + SymbolKind Kind = Record.kind(); + LLVM_DEBUG({ + W.printNumber("Offset", Offset); + W.printEnum("Begin Kind", unsigned(Kind), getSymbolTypeNames()); + }); + + if (options().getInternalTag()) + Shared->SymbolKinds.insert(Kind); + + CurrentElement = LogicalVisitor->createElement(Kind); + if (!CurrentElement) { + LLVM_DEBUG({ + // We have an unsupported Symbol or Type Record. + // W.printEnum("Kind ignored", unsigned(Kind), getSymbolTypeNames()); + }); + return Error::success(); + } + + // Offset carried by the traversal routines when dealing with streams. + CurrentOffset = Offset; + IsCompileUnit = false; + if (!CurrentElement->getOffsetFromTypeIndex()) + CurrentElement->setOffset(Offset); + if (symbolOpensScope(Kind) || (IsCompileUnit = symbolIsCompileUnit(Kind))) { + assert(CurrentScope && "Invalid scope!"); + LogicalVisitor->addElement(CurrentScope, IsCompileUnit); + } else { + if (CurrentSymbol) + LogicalVisitor->addElement(CurrentSymbol); + if (CurrentType) + LogicalVisitor->addElement(CurrentType); + } + + return Error::success(); +} + +Error LVSymbolVisitor::visitSymbolEnd(CVSymbol &Record) { + SymbolKind Kind = Record.kind(); + LLVM_DEBUG( + { W.printEnum("End Kind", unsigned(Kind), getSymbolTypeNames()); }); + + if (symbolEndsScope(Kind)) { + LogicalVisitor->popScope(); + } + + return Error::success(); +} + +Error LVSymbolVisitor::visitUnknownSymbol(CVSymbol &Record) { + LLVM_DEBUG({ W.printNumber("Length", Record.length()); }); + return Error::success(); +} + +// S_BLOCK32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, BlockSym &Block) { + LLVM_DEBUG({ + W.printHex("CodeSize", Block.CodeSize); + W.printHex("Segment", Block.Segment); + W.printString("BlockName", Block.Name); + }); + + if (LVScope *Scope = CurrentScope) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Block.getRelocationOffset(), Block.CodeOffset, + &LinkageName); + Scope->setLinkageName(LinkageName); + + if (options().getGeneralCollectRanges()) { + // Record converted segment::offset addressing for this scope. + LVAddress Addendum = Reader->getSymbolTableAddress(LinkageName); + LVAddress LowPC = + Reader->linearAddress(Block.Segment, Block.CodeOffset, Addendum); + LVAddress HighPC = LowPC + Block.CodeSize - 1; + Scope->addObject(LowPC, HighPC); + } + } + + return Error::success(); +} + +// S_BPREL32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + BPRelativeSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printNumber("Offset", Local.Offset); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Local.Name); + // From the MS_Symbol_Type.pdf documentation (S_BPREL32): + // This symbol specifies symbols that are allocated on the stack for a + // procedure. For C and C++, these include the actual function parameters + // and the local non-static variables of functions. + // However, the offset for 'this' comes as a negative value. + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + if (Local.Name.equals("this")) { + Symbol->setIsParameter(); + Symbol->setIsArtificial(); + } else { + // Determine symbol kind. + bool(Local.Offset > 0) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + + // FIXME: Currently the algorithm used to scope lambda functions is + // incorrect. Before we allocate the type at this scope, check if is + // already allocated in other scope. + if (!Element->getParentScope()) { + Parent->addElement(Element); + Element->updateLevel(Parent); + } + } + Symbol->setType(Element); + } + + return Error::success(); +} + +// S_REGREL32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + RegRelativeSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printNumber("Offset", Local.Offset); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Local.Name); + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + // Check for the 'this' symbol. + if (Local.Name.equals("this")) { + Symbol->setIsArtificial(); + Symbol->setIsParameter(); + } else { + // Determine symbol kind. + determineSymbolKind(Symbol, Local.Register); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + + // FIXME: Currently the algorithm used to scope lambda functions is + // incorrect. Before we allocate the type at this scope, check if is + // already allocated in other scope. + if (!Element->getParentScope()) { + Parent->addElement(Element); + Element->updateLevel(Parent); + } + } + Symbol->setType(Element); + } + + return Error::success(); +} + +// S_BUILDINFO +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &CVR, + BuildInfoSym &BuildInfo) { + LLVM_DEBUG({ printTypeIndex("BuildId", BuildInfo.BuildId); }); + + CVType CVBuildType = Ids.getType(BuildInfo.BuildId); + if (Error Err = LogicalVisitor->finishVisitation( + CVBuildType, BuildInfo.BuildId, Reader->getCompileUnit())) + return Err; + + return Error::success(); +} + +// S_COMPILE2 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + Compile2Sym &Compile2) { + LLVM_DEBUG({ + W.printEnum("Language", uint8_t(Compile2.getLanguage()), + getSourceLanguageNames()); + W.printFlags("Flags", uint32_t(Compile2.getFlags()), + getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile2.Machine), getCPUTypeNames()); + W.printString("VersionName", Compile2.Version); + }); + + // MSVC generates the following sequence for a CodeView module: + // S_OBJNAME --> Set 'CurrentObjectName'. + // S_COMPILE2 --> Set the compile unit name using 'CurrentObjectName'. + // ... + // S_BUILDINFO --> Extract the source name. + // + // Clang generates the following sequence for a CodeView module: + // S_COMPILE2 --> Set the compile unit name to empty string. + // ... + // S_BUILDINFO --> Extract the source name. + // + // For both toolchains, update the compile unit name from S_BUILDINFO. + if (LVScope *Scope = CurrentScope) { + // The name of the CU, was extracted from the 'BuildInfo' subsection. + Reader->setCompileUnitCPUType(Compile2.Machine); + Scope->setName(CurrentObjectName); + if (options().getAttributeProducer()) + Scope->setProducer(Compile2.Version); + getReader().isSystemEntry(Scope, CurrentObjectName); + + // The line records in CodeView are recorded per Module ID. Update + // the relationship between the current CU and the Module ID. + Reader->addModule(Scope); + + // Updated the collected strings with their associated compile unit. + Shared->StringRecords.addFilenames(Reader->getCompileUnit()); + } + + // Clear any previous ObjectName. + CurrentObjectName = ""; + return Error::success(); +} + +// S_COMPILE3 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + Compile3Sym &Compile3) { + LLVM_DEBUG({ + W.printEnum("Language", uint8_t(Compile3.getLanguage()), + getSourceLanguageNames()); + W.printFlags("Flags", uint32_t(Compile3.getFlags()), + getCompileSym3FlagNames()); + W.printEnum("Machine", unsigned(Compile3.Machine), getCPUTypeNames()); + W.printString("VersionName", Compile3.Version); + }); + + // MSVC generates the following sequence for a CodeView module: + // S_OBJNAME --> Set 'CurrentObjectName'. + // S_COMPILE3 --> Set the compile unit name using 'CurrentObjectName'. + // ... + // S_BUILDINFO --> Extract the source name. + // + // Clang generates the following sequence for a CodeView module: + // S_COMPILE3 --> Set the compile unit name to empty string. + // ... + // S_BUILDINFO --> Extract the source name. + // + // For both toolchains, update the compile unit name from S_BUILDINFO. + if (LVScope *Scope = CurrentScope) { + // The name of the CU, was extracted from the 'BuildInfo' subsection. + Reader->setCompileUnitCPUType(Compile3.Machine); + Scope->setName(CurrentObjectName); + if (options().getAttributeProducer()) + Scope->setProducer(Compile3.Version); + getReader().isSystemEntry(Scope, CurrentObjectName); + + // The line records in CodeView are recorded per Module ID. Update + // the relationship between the current CU and the Module ID. + Reader->addModule(Scope); + + // Updated the collected strings with their associated compile unit. + Shared->StringRecords.addFilenames(Reader->getCompileUnit()); + } + + // Clear any previous ObjectName. + CurrentObjectName = ""; + return Error::success(); +} + +// S_CONSTANT, S_MANCONSTANT +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + ConstantSym &Constant) { + LLVM_DEBUG({ + printTypeIndex("Type", Constant.Type); + W.printNumber("Value", Constant.Value); + W.printString("Name", Constant.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Constant.Name); + Symbol->setType(LogicalVisitor->getElement(StreamTPI, Constant.Type)); + Symbol->resetIncludeInPrint(); + } + + return Error::success(); +} + +// S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, + DefRangeFramePointerRelFullScopeSym &DefRangeFramePointerRelFullScope) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printNumber("Offset", DefRangeFramePointerRelFullScope.Offset); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Offset, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE); + + uint64_t Operand1 = DefRangeFramePointerRelFullScope.Offset; + Symbol->addLocation(Attr, 0, 0, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_FRAMEPOINTER_REL +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeFramePointerRelSym &DefRangeFramePointerRel) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printNumber("Offset", DefRangeFramePointerRel.Hdr.Offset); + printLocalVariableAddrRange(DefRangeFramePointerRel.Range, + DefRangeFramePointerRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeFramePointerRel.Gaps); + }); + + // We are expecting the following sequence: + // 128 | S_LOCAL [size = 20] `ParamBar` + // ... + // 148 | S_DEFRANGE_FRAMEPOINTER_REL [size = 16] + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Offset, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_FRAMEPOINTER_REL); + uint64_t Operand1 = DefRangeFramePointerRel.Hdr.Offset; + + LocalVariableAddrRange Range = DefRangeFramePointerRel.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_REGISTER_REL +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeRegisterRelSym &DefRangeRegisterRel) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printBoolean("HasSpilledUDTMember", + DefRangeRegisterRel.hasSpilledUDTMember()); + W.printNumber("OffsetInParent", DefRangeRegisterRel.offsetInParent()); + W.printNumber("BasePointerOffset", + DefRangeRegisterRel.Hdr.BasePointerOffset); + printLocalVariableAddrRange(DefRangeRegisterRel.Range, + DefRangeRegisterRel.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegisterRel.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, Offset]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_REGISTER_REL); + uint64_t Operand1 = DefRangeRegisterRel.Hdr.Register; + uint64_t Operand2 = DefRangeRegisterRel.Hdr.BasePointerOffset; + + LocalVariableAddrRange Range = DefRangeRegisterRel.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, Operand2); + } + + return Error::success(); +} + +// S_DEFRANGE_REGISTER +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeRegisterSym &DefRangeRegister) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printEnum("Register", uint16_t(DefRangeRegister.Hdr.Register), + getRegisterNames(Reader->getCompileUnitCPUType())); + W.printNumber("MayHaveNoName", DefRangeRegister.Hdr.MayHaveNoName); + printLocalVariableAddrRange(DefRangeRegister.Range, + DefRangeRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeRegister.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE_REGISTER); + uint64_t Operand1 = DefRangeRegister.Hdr.Register; + + Symbol->addLocation(Attr, 0, 0, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_SUBFIELD_REGISTER +Error LVSymbolVisitor::visitKnownRecord( + CVSymbol &Record, DefRangeSubfieldRegisterSym &DefRangeSubfieldRegister) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + W.printEnum("Register", uint16_t(DefRangeSubfieldRegister.Hdr.Register), + getRegisterNames(Reader->getCompileUnitCPUType())); + W.printNumber("MayHaveNoName", DefRangeSubfieldRegister.Hdr.MayHaveNoName); + W.printNumber("OffsetInParent", + DefRangeSubfieldRegister.Hdr.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfieldRegister.Range, + DefRangeSubfieldRegister.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfieldRegister.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Register, 0]. + dwarf::Attribute Attr = + dwarf::Attribute(SymbolKind::S_DEFRANGE_SUBFIELD_REGISTER); + uint64_t Operand1 = DefRangeSubfieldRegister.Hdr.Register; + + Symbol->addLocation(Attr, 0, 0, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE_SUBFIELD +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeSubfieldSym &DefRangeSubfield) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + if (ObjDelegate) { + DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable(); + auto ExpectedProgram = Strings.getString(DefRangeSubfield.Program); + if (!ExpectedProgram) { + consumeError(ExpectedProgram.takeError()); + return llvm::make_error( + "String table offset outside of bounds of String Table!"); + } + W.printString("Program", *ExpectedProgram); + } + W.printNumber("OffsetInParent", DefRangeSubfield.OffsetInParent); + printLocalVariableAddrRange(DefRangeSubfield.Range, + DefRangeSubfield.getRelocationOffset()); + printLocalVariableAddrGap(DefRangeSubfield.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Program, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE_SUBFIELD); + uint64_t Operand1 = DefRangeSubfield.Program; + + LocalVariableAddrRange Range = DefRangeSubfield.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_DEFRANGE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + DefRangeSym &DefRange) { + // Defranges don't have types, just registers and code offsets. + LLVM_DEBUG({ + if (LocalSymbol) + W.getOStream() << formatv("Symbol: {0}, ", LocalSymbol->getName()); + + if (ObjDelegate) { + DebugStringTableSubsectionRef Strings = ObjDelegate->getStringTable(); + auto ExpectedProgram = Strings.getString(DefRange.Program); + if (!ExpectedProgram) { + consumeError(ExpectedProgram.takeError()); + return llvm::make_error( + "String table offset outside of bounds of String Table!"); + } + W.printString("Program", *ExpectedProgram); + } + printLocalVariableAddrRange(DefRange.Range, DefRange.getRelocationOffset()); + printLocalVariableAddrGap(DefRange.Gaps); + }); + + if (LVSymbol *Symbol = LocalSymbol) { + Symbol->setHasCodeViewLocation(); + LocalSymbol = nullptr; + + // Add location debug location. Operands: [Program, 0]. + dwarf::Attribute Attr = dwarf::Attribute(SymbolKind::S_DEFRANGE); + uint64_t Operand1 = DefRange.Program; + + LocalVariableAddrRange Range = DefRange.Range; + LVAddress Address = + Reader->linearAddress(Range.ISectStart, Range.OffsetStart); + + Symbol->addLocation(Attr, Address, Address + Range.Range, 0, 0); + Symbol->addLocationOperands(LVSmall(Attr), Operand1, /*Operand2=*/0); + } + + return Error::success(); +} + +// S_FRAMEPROC +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + FrameProcSym &FrameProc) { + if (LVScope *Function = LogicalVisitor->getReaderScope()) { + // S_FRAMEPROC contains extra information for the function described + // by any of the previous generated records: + // S_GPROC32, S_LPROC32, S_LPROC32_ID, S_GPROC32_ID. + + // The generated sequence is: + // S_GPROC32_ID ... + // S_FRAMEPROC ... + + // Collect additional inline flags for the current scope function. + FrameProcedureOptions Flags = FrameProc.Flags; + if (FrameProcedureOptions::MarkedInline == + (Flags & FrameProcedureOptions::MarkedInline)) + Function->setInlineCode(dwarf::DW_INL_declared_inlined); + if (FrameProcedureOptions::Inlined == + (Flags & FrameProcedureOptions::Inlined)) + Function->setInlineCode(dwarf::DW_INL_inlined); + + // To determine the symbol kind for any symbol declared in that function, + // we can access the S_FRAMEPROC for the parent scope function. It contains + // information about the local fp and param fp registers and compare with + // the register in the S_REGREL32 to get a match. + codeview::CPUType CPU = Reader->getCompileUnitCPUType(); + LocalFrameRegister = FrameProc.getLocalFramePtrReg(CPU); + ParamFrameRegister = FrameProc.getParamFramePtrReg(CPU); + } + + return Error::success(); +} + +// S_GDATA32, S_LDATA32, S_LMANDATA, S_GMANDATA +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, DataSym &Data) { + LLVM_DEBUG({ + printTypeIndex("Type", Data.Type); + W.printString("DisplayName", Data.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Data.getRelocationOffset(), Data.DataOffset, + &LinkageName); + + Symbol->setName(Data.Name); + Symbol->setLinkageName(LinkageName); + + // The MSVC generates local data as initialization for aggregates. It + // contains the address for an initialization function. + // The symbols contains the '$initializer$' pattern. Allow them only if + // the '--internal=system' option is given. + // 0 | S_LDATA32 `Struct$initializer$` + // type = 0x1040 (void ()*) + if (getReader().isSystemEntry(Symbol) && !options().getAttributeSystem()) { + Symbol->resetIncludeInPrint(); + return Error::success(); + } + + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Data.Name)) { + // The variable is already at different scope. In order to reflect + // the correct parent, move it to the namespace. + if (Symbol->getParentScope()->removeElement(Symbol)) + Namespace->addElement(Symbol); + } + + Symbol->setType(LogicalVisitor->getElement(StreamTPI, Data.Type)); + if (Record.kind() == SymbolKind::S_GDATA32) + Symbol->setIsExternal(); + } + + return Error::success(); +} + +// S_INLINESITE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + InlineSiteSym &InlineSite) { + LLVM_DEBUG({ printTypeIndex("Inlinee", InlineSite.Inlinee); }); + + if (LVScope *InlinedFunction = CurrentScope) { + LVScope *AbstractFunction = new LVScopeFunction(); + if (AbstractFunction) { + AbstractFunction->setIsSubprogram(); + AbstractFunction->setTag(dwarf::DW_TAG_subprogram); + AbstractFunction->setInlineCode(dwarf::DW_INL_inlined); + AbstractFunction->setIsInlinedAbstract(); + InlinedFunction->setReference(AbstractFunction); + + LogicalVisitor->startProcessArgumentList(); + // 'Inlinee' is a Type ID. + CVType CVFunctionType = Ids.getType(InlineSite.Inlinee); + if (Error Err = LogicalVisitor->finishVisitation( + CVFunctionType, InlineSite.Inlinee, AbstractFunction)) + return Err; + LogicalVisitor->stopProcessArgumentList(); + + // For inlined functions set the linkage name to be the same as + // the name. It used to find their lines and ranges. + StringRef Name = AbstractFunction->getName(); + InlinedFunction->setName(Name); + InlinedFunction->setLinkageName(Name); + + // Process annotation bytes to calculate code and line offsets. + if (Error Err = LogicalVisitor->inlineSiteAnnotation( + AbstractFunction, InlinedFunction, InlineSite)) + return Err; + } + } + + return Error::success(); +} + +// S_LOCAL +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, LocalSym &Local) { + LLVM_DEBUG({ + printTypeIndex("Type", Local.Type); + W.printFlags("Flags", uint16_t(Local.Flags), getLocalFlagNames()); + W.printString("VarName", Local.Name); + }); + + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Local.Name); + + // Symbol was created as 'variable'; determine its real kind. + Symbol->resetIsVariable(); + + // Be sure the 'this' symbol is marked as 'compiler generated'. + if (bool(Local.Flags & LocalSymFlags::IsCompilerGenerated) || + Local.Name.equals("this")) { + Symbol->setIsArtificial(); + Symbol->setIsParameter(); + } else { + bool(Local.Flags & LocalSymFlags::IsParameter) ? Symbol->setIsParameter() + : Symbol->setIsVariable(); + } + + // Update correct debug information tag. + if (Symbol->getIsParameter()) + Symbol->setTag(dwarf::DW_TAG_formal_parameter); + + LVElement *Element = LogicalVisitor->getElement(StreamTPI, Local.Type); + if (Element && Element->getIsScoped()) { + // We have a local type. Find its parent function. + LVScope *Parent = Symbol->getFunctionParent(); + // The element representing the type has been already finalized. If + // the type is an aggregate type, its members have been already added. + // As the type is local, its level will be changed. + Parent->addElement(Element); + Element->updateLevel(Parent); + } + Symbol->setType(Element); + + // The CodeView records (S_DEFFRAME_*) describing debug location for + // this symbol, do not have any direct reference to it. Those records + // are emitted after this symbol. Record the current symbol. + LocalSymbol = Symbol; + } + + return Error::success(); +} + +// S_OBJNAME +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, ObjNameSym &ObjName) { + LLVM_DEBUG({ + W.printHex("Signature", ObjName.Signature); + W.printString("ObjectName", ObjName.Name); + }); + + CurrentObjectName = ObjName.Name; + return Error::success(); +} + +// S_GPROC32, S_LPROC32, S_LPROC32_ID, S_GPROC32_ID +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, ProcSym &Proc) { + if (InFunctionScope) + return llvm::make_error("Visiting a ProcSym while inside " + "function scope!"); + + InFunctionScope = true; + + LLVM_DEBUG({ + printTypeIndex("FunctionType", Proc.FunctionType); + W.printHex("Segment", Proc.Segment); + W.printFlags("Flags", static_cast(Proc.Flags), + getProcSymFlagNames()); + W.printString("DisplayName", Proc.Name); + }); + + // Clang and Microsoft generated different debug information records: + // For functions definitions: + // Clang: S_GPROC32 -> LF_FUNC_ID -> LF_PROCEDURE + // Microsoft: S_GPROC32 -> LF_PROCEDURE + + // For member function definition: + // Clang: S_GPROC32 -> LF_MFUNC_ID -> LF_MFUNCTION + // Microsoft: S_GPROC32 -> LF_MFUNCTION + // In order to support both sequences, if we found LF_FUNCTION_ID, just + // get the TypeIndex for LF_PROCEDURE. + + // For the given test case, we have the sequence: + // namespace NSP_local { + // void foo_local() { + // } + // } + // + // 0x1000 | LF_STRING_ID String: NSP_local + // 0x1002 | LF_PROCEDURE + // return type = 0x0003 (void), # args = 0, param list = 0x1001 + // calling conv = cdecl, options = None + // 0x1003 | LF_FUNC_ID + // name = foo_local, type = 0x1002, parent scope = 0x1000 + // 0 | S_GPROC32_ID `NSP_local::foo_local` + // type = `0x1003 (foo_local)` + // 0x1004 | LF_STRING_ID String: suite + // 0x1005 | LF_STRING_ID String: suite_local.cpp + // + // The LF_STRING_ID can hold different information: + // 0x1000 - The enclosing namespace. + // 0x1004 - The compile unit directory name. + // 0x1005 - The compile unit name. + // + // Before deducting its scope, we need to evaluate its type and create any + // associated namespaces. + if (LVScope *Function = CurrentScope) { + StringRef LinkageName; + if (ObjDelegate) + ObjDelegate->getLinkageName(Proc.getRelocationOffset(), Proc.CodeOffset, + &LinkageName); + + // The line table can be accessed using the linkage name. + Reader->addToSymbolTable(LinkageName, Function); + Function->setName(Proc.Name); + Function->setLinkageName(LinkageName); + + if (options().getGeneralCollectRanges()) { + // Record converted segment::offset addressing for this scope. + LVAddress Addendum = Reader->getSymbolTableAddress(LinkageName); + LVAddress LowPC = + Reader->linearAddress(Proc.Segment, Proc.CodeOffset, Addendum); + LVAddress HighPC = LowPC + Proc.CodeSize - 1; + Function->addObject(LowPC, HighPC); + + // If the scope is a function, add it to the public names. + if ((options().getAttributePublics() || options().getPrintAnyLine()) && + !Function->getIsInlinedFunction()) + Reader->getCompileUnit()->addPublicName(Function, LowPC, HighPC); + } + + if (Function->getIsSystem() && !options().getAttributeSystem()) { + Function->resetIncludeInPrint(); + return Error::success(); + } + + TypeIndex TIFunctionType = Proc.FunctionType; + if (TIFunctionType.isSimple()) + Function->setType(LogicalVisitor->getElement(StreamTPI, TIFunctionType)); + else { + // We have to detect the correct stream, using the lexical parent + // name, as there is not other obvious way to get the stream. + // Normal function: LF_FUNC_ID (TPI)/(IPI) + // LF_PROCEDURE (TPI) + // Lambda function: LF_MFUNCTION (TPI) + // Member function: LF_MFUNC_ID (TPI)/(IPI) + + StringRef OuterComponent; + std::tie(OuterComponent, std::ignore) = getInnerComponent(Proc.Name); + TypeIndex TI = Shared->ForwardReferences.find(OuterComponent); + + llvm::Optional CVFunctionType; + auto GetRecordType = [&]() -> bool { + CVFunctionType = Ids.tryGetType(TIFunctionType); + if (!CVFunctionType) + return false; + + if (TI.isNoneType()) + // Normal function. + if (CVFunctionType->kind() == LF_FUNC_ID) + return true; + + // Member function. + return (CVFunctionType->kind() == LF_MFUNC_ID); + }; + + // We can have a LF_FUNC_ID, LF_PROCEDURE or LF_MFUNCTION. + if (!GetRecordType()) { + CVFunctionType = Types.tryGetType(TIFunctionType); + if (!CVFunctionType) + return llvm::make_error("Invalid type index"); + } + + if (Error Err = LogicalVisitor->finishVisitation( + *CVFunctionType, TIFunctionType, Function)) + return Err; + } + + if (Record.kind() == SymbolKind::S_GPROC32 || + Record.kind() == SymbolKind::S_GPROC32_ID) + Function->setIsExternal(); + + // We don't have a way to see if the symbol is compiler generated. Use + // the linkage name, to detect `scalar deleting destructor' functions. + std::string DemangledSymbol = demangle(std::string(LinkageName)); + if (DemangledSymbol.find("scalar deleting dtor") != std::string::npos) + Function->setIsArtificial(); + } + + return Error::success(); +} + +// S_END +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + ScopeEndSym &ScopeEnd) { + InFunctionScope = false; + return Error::success(); +} + +// S_THUNK32 +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, Thunk32Sym &Thunk) { + if (InFunctionScope) + return llvm::make_error("Visiting a Thunk32Sym while inside " + "function scope!"); + + InFunctionScope = true; + + LLVM_DEBUG({ + W.printHex("Segment", Thunk.Segment); + W.printString("Name", Thunk.Name); + }); + + if (LVScope *Function = CurrentScope) + Function->setName(Thunk.Name); + + return Error::success(); +} + +// S_UDT, S_COBOLUDT +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, UDTSym &UDT) { + LLVM_DEBUG({ + printTypeIndex("Type", UDT.Type); + W.printString("UDTName", UDT.Name); + }); + + if (LVType *Type = CurrentType) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(UDT.Name)) { + if (Type->getParentScope()->removeElement(Type)) + Namespace->addElement(Type); + } + + Type->setName(UDT.Name); + + // We have to determine if the typedef is a real C/C++ definition or is + // the S_UDT record that describe all the user defined types. + // 0 | S_UDT `Name` original type = 0x1009 + // 0x1009 | LF_STRUCTURE `Name` + // Ignore type definitions for RTTI types: + // _s__RTTIBaseClassArray, _s__RTTIBaseClassDescriptor, + // _s__RTTICompleteObjectLocator, _s__RTTIClassHierarchyDescriptor. + if (getReader().isSystemEntry(Type)) + Type->resetIncludeInPrint(); + else { + StringRef RecordName = getRecordName(Types, UDT.Type); + if (UDT.Name.equals(RecordName)) + Type->resetIncludeInPrint(); + Type->setType(LogicalVisitor->getElement(StreamTPI, UDT.Type)); + } + } + + return Error::success(); +} + +// S_UNAMESPACE +Error LVSymbolVisitor::visitKnownRecord(CVSymbol &Record, + UsingNamespaceSym &UN) { + LLVM_DEBUG({ W.printString("Namespace", UN.Name); }); + return Error::success(); +} + +#undef DEBUG_TYPE +#define DEBUG_TYPE "CodeViewLogicalVisitor" + +//===----------------------------------------------------------------------===// +// Logical visitor. +//===----------------------------------------------------------------------===// +LVLogicalVisitor::LVLogicalVisitor(LVCodeViewReader *Reader, ScopedPrinter &W, + InputFile &Input) + : Reader(Reader), W(W), Input(Input) { + // The LogicalVisitor connects the CodeViewReader with the visitors that + // traverse the types, symbols, etc. Do any initialization that is needed. + Shared = new LVShared(Reader, this); +} +LVLogicalVisitor::~LVLogicalVisitor() { delete Shared; } + +void LVLogicalVisitor::printTypeIndex(StringRef FieldName, TypeIndex TI, + uint32_t StreamIdx) { + codeview::printTypeIndex(W, FieldName, TI, + StreamIdx == StreamTPI ? types() : ids()); +} + +void LVLogicalVisitor::printTypeBegin(CVType &Record, TypeIndex TI, + LVElement *Element, uint32_t StreamIdx) { + W.getOStream() << "\n"; + W.startLine() << formatTypeLeafKind(Record.kind()); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")"; + W.getOStream() << " {\n"; + W.indent(); + W.printEnum("TypeLeafKind", unsigned(Record.kind()), + makeArrayRef(LeafTypeNames)); + printTypeIndex("TI", TI, StreamIdx); + W.startLine() << "Element: " << HexNumber(Element->getOffset()) << " " + << Element->getName() << "\n"; +} + +void LVLogicalVisitor::printTypeEnd(CVType &Record) { + W.unindent(); + W.startLine() << "}\n"; +} + +void LVLogicalVisitor::printMemberBegin(CVMemberRecord &Record, TypeIndex TI, + LVElement *Element, + uint32_t StreamIdx) { + W.getOStream() << "\n"; + W.startLine() << formatTypeLeafKind(Record.Kind); + W.getOStream() << " (" << HexNumber(TI.getIndex()) << ")"; + W.getOStream() << " {\n"; + W.indent(); + W.printEnum("TypeLeafKind", unsigned(Record.Kind), + makeArrayRef(LeafTypeNames)); + printTypeIndex("TI", TI, StreamIdx); + W.startLine() << "Element: " << HexNumber(Element->getOffset()) << " " + << Element->getName() << "\n"; +} + +void LVLogicalVisitor::printMemberEnd(CVMemberRecord &Record) { + W.unindent(); + W.startLine() << "}\n"; +} + +Error LVLogicalVisitor::visitUnknownType(CVType &Record, TypeIndex TI) { + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamTPI); + W.printNumber("Length", uint32_t(Record.content().size())); + }); + return Error::success(); +} + +// LF_ARGLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ArgListRecord &Args, + TypeIndex TI, LVElement *Element) { + ArrayRef Indices = Args.getIndices(); + uint32_t Size = Indices.size(); + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("NumArgs", Size); + ListScope Arguments(W, "Arguments"); + for (uint32_t I = 0; I < Size; ++I) + printTypeIndex("ArgType", Indices[I], StreamTPI); + printTypeEnd(Record); + }); + + LVScope *Function = static_cast(Element); + for (uint32_t Index = 0; Index < Size; ++Index) { + TypeIndex ParameterType = Indices[Index]; + createParameter(ParameterType, StringRef(), Function); + } + + return Error::success(); +} + +// LF_ARRAY (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ArrayRecord &AT, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ElementType", AT.getElementType(), StreamTPI); + printTypeIndex("IndexType", AT.getIndexType(), StreamTPI); + W.printNumber("SizeOf", AT.getSize()); + W.printString("Name", AT.getName()); + printTypeEnd(Record); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + LVScopeArray *Array = static_cast(Element); + if (!Array) + return Error::success(); + + Reader->getCompileUnit()->addElement(Array); + TypeIndex TIElementType = AT.getElementType(); + + LVType *PrevSubrange = nullptr; + LazyRandomTypeCollection &Types = types(); + + // As the logical view is modeled on DWARF, for each dimension we have to + // create a DW_TAG_subrange_type, with dimension size. + // The subrange type can be: unsigned __int32 or unsigned __int64. + auto AddSubrangeType = [&](ArrayRecord &AR) { + LVType *Subrange = new LVTypeSubrange(); + Subrange->setTag(dwarf::DW_TAG_subrange_type); + Subrange->setType(getElement(StreamTPI, AR.getIndexType())); + Subrange->setCount(AR.getSize()); + Subrange->setOffset( + TIElementType.isSimple() + ? (uint32_t)(TypeLeafKind)TIElementType.getSimpleKind() + : TIElementType.getIndex()); + Array->addElement(Subrange); + + if (PrevSubrange) + if (int64_t Count = Subrange->getCount()) + PrevSubrange->setCount(PrevSubrange->getCount() / Count); + PrevSubrange = Subrange; + }; + + // Preserve the original TypeIndex; it would be updated in the case of: + // - The array type contains qualifiers. + // - In multidimensional arrays, the last LF_ARRAY entry contains the type. + TypeIndex TIArrayType; + + // For each dimension in the array, there is a LF_ARRAY entry. The last + // entry contains the array type, which can be a LF_MODIFIER in the case + // of the type being modified by a qualifier (const, etc). + ArrayRecord AR(AT); + CVType CVEntry = Record; + while (CVEntry.kind() == LF_ARRAY) { + // Create the subrange information, required by the logical view. Once + // the array has been processed, the dimension sizes will updated, as + // the sizes are a progression. For instance: + // sizeof(int) = 4 + // int Array[2]; Sizes: 8 Dim: 8 / 4 -> [2] + // int Array[2][3]; Sizes: 24, 12 Dim: 24 / 12 -> [2] + // Dim: 12 / 4 -> [3] + // int Array[2][3][4]; sizes: 96, 48, 16 Dim: 96 / 48 -> [2] + // Dim: 48 / 16 -> [3] + // Dim: 16 / 4 -> [4] + AddSubrangeType(AR); + TIArrayType = TIElementType; + + // The current ElementType can be a modifier, in which case we need to + // get the type being modified. + // If TypeIndex is not a simple type, check if we have a qualified type. + if (!TIElementType.isSimple()) { + CVType CVElementType = Types.getType(TIElementType); + if (CVElementType.kind() == LF_MODIFIER) { + LVElement *QualifiedType = + Shared->TypeRecords.find(StreamTPI, TIElementType); + if (Error Err = + finishVisitation(CVElementType, TIElementType, QualifiedType)) + return Err; + // Get the TypeIndex of the type that the LF_MODIFIER modifies. + TIElementType = getModifiedType(CVElementType); + } + } + // Ends the traversal, as we have reached a simple type (int, char, etc). + if (TIElementType.isSimple()) + break; + + // Read next dimension linked entry, if any. + CVEntry = Types.getType(TIElementType); + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(CVEntry), AR)) { + consumeError(std::move(Err)); + break; + } + TIElementType = AR.getElementType(); + // NOTE: The typeindex has a value of: 0x0280.0000 + getTrueType(TIElementType); + } + + Array->setName(AT.getName()); + TIArrayType = Shared->ForwardReferences.remap(TIArrayType); + Array->setType(getElement(StreamTPI, TIArrayType)); + + if (PrevSubrange) + // In the case of an aggregate type (class, struct, union, interface), + // get the aggregate size. As the original record is pointing to its + // reference, we have to update it. + if (uint64_t Size = + isAggregate(CVEntry) + ? getSizeInBytesForTypeRecord(Types.getType(TIArrayType)) + : getSizeInBytesForTypeIndex(TIElementType)) + PrevSubrange->setCount(PrevSubrange->getCount() / Size); + + return Error::success(); +} + +// LF_BITFIELD (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, BitFieldRecord &BF, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", TI, StreamTPI); + W.printNumber("BitSize", BF.getBitSize()); + W.printNumber("BitOffset", BF.getBitOffset()); + printTypeEnd(Record); + }); + + Element->setType(getElement(StreamTPI, BF.getType())); + Element->setBitSize(BF.getBitSize()); + return Error::success(); +} + +// LF_BUILDINFO (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, BuildInfoRecord &BI, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + W.printNumber("NumArgs", static_cast(BI.getArgs().size())); + ListScope Arguments(W, "Arguments"); + for (TypeIndex Arg : BI.getArgs()) + printTypeIndex("ArgType", Arg, StreamIPI); + printTypeEnd(Record); + }); + + // The given 'Element' refers to the current compilation unit. + // All the args are references into the TPI/IPI stream. + TypeIndex TIName = BI.getArgs()[BuildInfoRecord::BuildInfoArg::SourceFile]; + std::string Name = std::string(ids().getTypeName(TIName)); + + // There are cases where LF_BUILDINFO fields are empty. + if (!Name.empty()) + Element->setName(Name); + + return Error::success(); +} + +// LF_CLASS, LF_STRUCTURE, LF_INTERFACE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ClassRecord &Class, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("MemberCount", Class.getMemberCount()); + printTypeIndex("FieldList", Class.getFieldList(), StreamTPI); + printTypeIndex("DerivedFrom", Class.getDerivationList(), StreamTPI); + printTypeIndex("VShape", Class.getVTableShape(), StreamTPI); + W.printNumber("SizeOf", Class.getSize()); + W.printString("Name", Class.getName()); + if (Class.hasUniqueName()) + W.printString("UniqueName", Class.getUniqueName()); + printTypeEnd(Record); + }); + + if (Element->getIsFinalized()) + return Error::success(); + Element->setIsFinalized(); + + LVScopeAggregate *Scope = static_cast(Element); + if (!Scope) + return Error::success(); + + Scope->setName(Class.getName()); + if (Class.hasUniqueName()) + Scope->setLinkageName(Class.getUniqueName()); + + if (Class.isNested()) { + Scope->setIsNested(); + createParents(Class.getName(), Scope); + } + + if (Class.isScoped()) + Scope->setIsScoped(); + + // Nested types will be added to their parents at creation. The forward + // references are only processed to finish the referenced element creation. + if (!(Class.isNested() || Class.isScoped())) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Class.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + LazyRandomTypeCollection &Types = types(); + TypeIndex TIFieldList = Class.getFieldList(); + if (TIFieldList.isNoneType()) { + TypeIndex ForwardType = Shared->ForwardReferences.find(Class.getName()); + if (!ForwardType.isNoneType()) { + CVType CVReference = Types.getType(ForwardType); + TypeRecordKind RK = static_cast(CVReference.kind()); + ClassRecord ReferenceRecord(RK); + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(CVReference), ReferenceRecord)) + return Err; + TIFieldList = ReferenceRecord.getFieldList(); + } + } + + if (!TIFieldList.isNoneType()) { + // Pass down the TypeIndex 'TI' for the aggregate containing the field list. + CVType CVFieldList = Types.getType(TIFieldList); + if (Error Err = finishVisitation(CVFieldList, TI, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_ENUM (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, EnumRecord &Enum, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("NumEnumerators", Enum.getMemberCount()); + printTypeIndex("UnderlyingType", Enum.getUnderlyingType(), StreamTPI); + printTypeIndex("FieldListType", Enum.getFieldList(), StreamTPI); + W.printString("Name", Enum.getName()); + printTypeEnd(Record); + }); + + LVScopeEnumeration *Scope = static_cast(Element); + if (!Scope) + return Error::success(); + + if (Scope->getIsFinalized()) + return Error::success(); + Scope->setIsFinalized(); + + // Set the name, as in the case of nested, it would determine the relation + // to any potential parent, via the LF_NESTTYPE record. + Scope->setName(Enum.getName()); + if (Enum.hasUniqueName()) + Scope->setLinkageName(Enum.getUniqueName()); + + Scope->setType(getElement(StreamTPI, Enum.getUnderlyingType())); + + if (Enum.isNested()) { + Scope->setIsNested(); + createParents(Enum.getName(), Scope); + } + + if (Enum.isScoped()) { + Scope->setIsScoped(); + Scope->setIsEnumClass(); + } + + // Nested types will be added to their parents at creation. + if (!(Enum.isNested() || Enum.isScoped())) { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Enum.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + TypeIndex TIFieldList = Enum.getFieldList(); + if (!TIFieldList.isNoneType()) { + LazyRandomTypeCollection &Types = types(); + CVType CVFieldList = Types.getType(TIFieldList); + if (Error Err = finishVisitation(CVFieldList, TIFieldList, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_FIELDLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + FieldListRecord &FieldList, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + + if (Error Err = visitFieldListMemberStream(TI, Element, FieldList.Data)) + return Err; + + return Error::success(); +} + +// LF_FUNC_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, FuncIdRecord &Func, + TypeIndex TI, LVElement *Element) { + // ParentScope and FunctionType are references into the TPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("ParentScope", Func.getParentScope(), StreamTPI); + printTypeIndex("FunctionType", Func.getFunctionType(), StreamTPI); + W.printString("Name", Func.getName()); + printTypeEnd(Record); + }); + + // The TypeIndex (LF_PROCEDURE) returned by 'getFunctionType' is the + // function propotype, we need to use the function definition. + if (LVScope *FunctionDcl = static_cast(Element)) { + // For inlined functions, the inlined instance has been already processed + // (all its information is contained in the Symbols section). + // 'Element' points to the created 'abstract' (out-of-line) function. + // Use the parent scope information to allocate it to the correct scope. + LazyRandomTypeCollection &Types = types(); + TypeIndex TIParent = Func.getParentScope(); + if (FunctionDcl->getIsInlinedAbstract()) { + FunctionDcl->setName(Func.getName()); + if (TIParent.isNoneType()) + Reader->getCompileUnit()->addElement(FunctionDcl); + } + + if (!TIParent.isNoneType()) { + CVType CVParentScope = ids().getType(TIParent); + if (Error Err = finishVisitation(CVParentScope, TIParent, FunctionDcl)) + return Err; + } + + TypeIndex TIFunctionType = Func.getFunctionType(); + CVType CVFunctionType = Types.getType(TIFunctionType); + if (Error Err = + finishVisitation(CVFunctionType, TIFunctionType, FunctionDcl)) + return Err; + + FunctionDcl->setIsFinalized(); + } + + return Error::success(); +} + +// LF_LABEL (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, LabelRecord &LR, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_MFUNC_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, MemberFuncIdRecord &Id, + TypeIndex TI, LVElement *Element) { + // ClassType and FunctionType are references into the TPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("ClassType", Id.getClassType(), StreamTPI); + printTypeIndex("FunctionType", Id.getFunctionType(), StreamTPI); + W.printString("Name", Id.getName()); + printTypeEnd(Record); + }); + + LVScope *FunctionDcl = static_cast(Element); + if (FunctionDcl->getIsInlinedAbstract()) { + // For inlined functions, the inlined instance has been already processed + // (all its information is contained in the Symbols section). + // 'Element' points to the created 'abstract' (out-of-line) function. + // Use the parent scope information to allocate it to the correct scope. + if (LVScope *Class = static_cast( + Shared->TypeRecords.find(StreamTPI, Id.getClassType()))) + Class->addElement(FunctionDcl); + } + + TypeIndex TIFunctionType = Id.getFunctionType(); + CVType CVFunction = types().getType(TIFunctionType); + if (Error Err = finishVisitation(CVFunction, TIFunctionType, Element)) + return Err; + + return Error::success(); +} + +// LF_MFUNCTION (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + MemberFunctionRecord &MF, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ReturnType", MF.getReturnType(), StreamTPI); + printTypeIndex("ClassType", MF.getClassType(), StreamTPI); + printTypeIndex("ThisType", MF.getThisType(), StreamTPI); + W.printNumber("NumParameters", MF.getParameterCount()); + printTypeIndex("ArgListType", MF.getArgumentList(), StreamTPI); + W.printNumber("ThisAdjustment", MF.getThisPointerAdjustment()); + printTypeEnd(Record); + }); + + if (LVScope *MemberFunction = static_cast(Element)) { + LVElement *Class = getElement(StreamTPI, MF.getClassType()); + + MemberFunction->setIsFinalized(); + MemberFunction->setType(getElement(StreamTPI, MF.getReturnType())); + MemberFunction->setOffset(TI.getIndex()); + MemberFunction->setOffsetFromTypeIndex(); + + if (ProcessArgumentList) { + ProcessArgumentList = false; + + if (!MemberFunction->getIsStatic()) { + LVElement *ThisPointer = getElement(StreamTPI, MF.getThisType()); + // When creating the 'this' pointer, check if it points to a reference. + ThisPointer->setType(Class); + LVSymbol *This = + createParameter(ThisPointer, StringRef(), MemberFunction); + This->setIsArtificial(); + } + + // Create formal parameters. + LazyRandomTypeCollection &Types = types(); + CVType CVArguments = Types.getType(MF.getArgumentList()); + if (Error Err = finishVisitation(CVArguments, MF.getArgumentList(), + MemberFunction)) + return Err; + } + } + + return Error::success(); +} + +// LF_METHODLIST (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + MethodOverloadListRecord &Overloads, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeEnd(Record); + }); + + for (OneMethodRecord &Method : Overloads.Methods) { + CVMemberRecord Record; + Record.Kind = LF_METHOD; + Method.Name = OverloadedMethodName; + if (Error Err = visitKnownMember(Record, Method, TI, Element)) + return Err; + } + + return Error::success(); +} + +// LF_MODIFIER (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ModifierRecord &Mod, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ModifiedType", Mod.getModifiedType(), StreamTPI); + printTypeEnd(Record); + }); + + // Create the modified type, which will be attached to the type(s) that + // contains the modifiers. + LVElement *ModifiedType = getElement(StreamTPI, Mod.getModifiedType()); + + // At this point the types recording the qualifiers do not have a + // scope parent. They must be assigned to the current compile unit. + LVScopeCompileUnit *CompileUnit = Reader->getCompileUnit(); + + // The incomming element, does not have a defined kind. Use the given + // modifiers to complete its type. A type can have more than one modifier; + // in that case, we have to create an extra type to have the other modifier. + LVType *LastLink = static_cast(Element); + if (!LastLink->getParentScope()) + CompileUnit->addElement(LastLink); + + bool SeenModifier = false; + uint16_t Mods = static_cast(Mod.getModifiers()); + if (Mods & uint16_t(ModifierOptions::Const)) { + SeenModifier = true; + LastLink->setTag(dwarf::DW_TAG_const_type); + LastLink->setIsConst(); + LastLink->setName("const"); + } + if (Mods & uint16_t(ModifierOptions::Volatile)) { + if (SeenModifier) { + LVType *Volatile = new LVType(); + Volatile->setIsModifier(); + LastLink->setType(Volatile); + LastLink = Volatile; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_volatile_type); + LastLink->setIsVolatile(); + LastLink->setName("volatile"); + } + if (Mods & uint16_t(ModifierOptions::Unaligned)) { + if (SeenModifier) { + LVType *Unaligned = new LVType(); + Unaligned->setIsModifier(); + LastLink->setType(Unaligned); + LastLink = Unaligned; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_unaligned); + LastLink->setIsUnaligned(); + LastLink->setName("unaligned"); + } + + LastLink->setType(ModifiedType); + return Error::success(); +} + +// LF_POINTER (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, PointerRecord &Ptr, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("PointeeType", Ptr.getReferentType(), StreamTPI); + W.printNumber("IsFlat", Ptr.isFlat()); + W.printNumber("IsConst", Ptr.isConst()); + W.printNumber("IsVolatile", Ptr.isVolatile()); + W.printNumber("IsUnaligned", Ptr.isUnaligned()); + W.printNumber("IsRestrict", Ptr.isRestrict()); + W.printNumber("IsThisPtr&", Ptr.isLValueReferenceThisPtr()); + W.printNumber("IsThisPtr&&", Ptr.isRValueReferenceThisPtr()); + W.printNumber("SizeOf", Ptr.getSize()); + + if (Ptr.isPointerToMember()) { + const MemberPointerInfo &MI = Ptr.getMemberInfo(); + printTypeIndex("ClassType", MI.getContainingType(), StreamTPI); + } + printTypeEnd(Record); + }); + + // Type pointer is point to. + LVType *Pointer = static_cast(Element); + LVElement *Pointee = nullptr; + + PointerMode Mode = Ptr.getMode(); + Pointee = Ptr.isPointerToMember() + ? Shared->TypeRecords.find(StreamTPI, Ptr.getReferentType()) + : getElement(StreamTPI, Ptr.getReferentType()); + + // At this point the types recording the qualifiers do not have a + // scope parent. They must be assigned to the current compile unit. + LVScopeCompileUnit *CompileUnit = Reader->getCompileUnit(); + + // Order for the different modifiers: + // + // Const and volatile already processed. + bool SeenModifier = false; + LVType *LastLink = Pointer; + if (!LastLink->getParentScope()) + CompileUnit->addElement(LastLink); + + if (Ptr.isRestrict()) { + SeenModifier = true; + LVType *Restrict = new LVType(); + Restrict->setTag(dwarf::DW_TAG_restrict_type); + Restrict->setIsRestrict(); + Restrict->setName("restrict"); + LastLink->setType(Restrict); + LastLink = Restrict; + CompileUnit->addElement(LastLink); + } + if (Mode == PointerMode::LValueReference) { + if (SeenModifier) { + LVType *LReference = new LVType(); + LReference->setIsModifier(); + LastLink->setType(LReference); + LastLink = LReference; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_reference_type); + LastLink->setIsReference(); + LastLink->setName("&"); + } + if (Mode == PointerMode::RValueReference) { + if (SeenModifier) { + LVType *RReference = new LVType(); + RReference->setIsModifier(); + LastLink->setType(RReference); + LastLink = RReference; + CompileUnit->addElement(LastLink); + } + LastLink->setTag(dwarf::DW_TAG_rvalue_reference_type); + LastLink->setIsRvalueReference(); + LastLink->setName("&&"); + } + + // When creating the pointer, check if it points to a reference. + LastLink->setType(Pointee); + return Error::success(); +} + +// LF_PROCEDURE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, ProcedureRecord &Proc, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ReturnType", Proc.getReturnType(), StreamTPI); + W.printNumber("NumParameters", Proc.getParameterCount()); + printTypeIndex("ArgListType", Proc.getArgumentList(), StreamTPI); + printTypeEnd(Record); + }); + + // There is no need to traverse the argument list, as the CodeView format + // declares the parameters as a 'S_LOCAL' symbol tagged as parameter. + // Only process parameters when dealing with inline functions. + if (LVScope *FunctionDcl = static_cast(Element)) { + FunctionDcl->setType(getElement(StreamTPI, Proc.getReturnType())); + + if (ProcessArgumentList) { + ProcessArgumentList = false; + // Create formal parameters. + LazyRandomTypeCollection &Types = types(); + CVType CVArguments = Types.getType(Proc.getArgumentList()); + if (Error Err = finishVisitation(CVArguments, Proc.getArgumentList(), + FunctionDcl)) + return Err; + } + } + + return Error::success(); +} + +// LF_UNION (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, UnionRecord &Union, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("MemberCount", Union.getMemberCount()); + printTypeIndex("FieldList", Union.getFieldList(), StreamTPI); + W.printNumber("SizeOf", Union.getSize()); + W.printString("Name", Union.getName()); + if (Union.hasUniqueName()) + W.printString("UniqueName", Union.getUniqueName()); + printTypeEnd(Record); + }); + + LVScopeAggregate *Scope = static_cast(Element); + if (!Scope) + return Error::success(); + + if (Scope->getIsFinalized()) + return Error::success(); + Scope->setIsFinalized(); + + Scope->setName(Union.getName()); + if (Union.hasUniqueName()) + Scope->setLinkageName(Union.getUniqueName()); + + if (Union.isNested()) { + Scope->setIsNested(); + createParents(Union.getName(), Scope); + } else { + if (LVScope *Namespace = Shared->NamespaceDeduction.get(Union.getName())) + Namespace->addElement(Scope); + else + Reader->getCompileUnit()->addElement(Scope); + } + + if (!Union.getFieldList().isNoneType()) { + LazyRandomTypeCollection &Types = types(); + // Pass down the TypeIndex 'TI' for the aggregate containing the field list. + CVType CVFieldList = Types.getType(Union.getFieldList()); + if (Error Err = finishVisitation(CVFieldList, TI, Scope)) + return Err; + } + + return Error::success(); +} + +// LF_TYPESERVER2 (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, TypeServer2Record &TS, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printString("Guid", formatv("{0}", TS.getGuid()).str()); + W.printNumber("Age", TS.getAge()); + W.printString("Name", TS.getName()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_VFTABLE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, VFTableRecord &VFT, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + printTypeIndex("CompleteClass", VFT.getCompleteClass(), StreamTPI); + printTypeIndex("OverriddenVFTable", VFT.getOverriddenVTable(), StreamTPI); + W.printHex("VFPtrOffset", VFT.getVFPtrOffset()); + W.printString("VFTableName", VFT.getName()); + for (const StringRef &N : VFT.getMethodNames()) + W.printString("MethodName", N); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_VTSHAPE (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + VFTableShapeRecord &Shape, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printNumber("VFEntryCount", Shape.getEntryCount()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_SUBSTR_LIST (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + StringListRecord &Strings, + TypeIndex TI, LVElement *Element) { + // All the indices are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + ArrayRef Indices = Strings.getIndices(); + uint32_t Size = Indices.size(); + W.printNumber("NumStrings", Size); + ListScope Arguments(W, "Strings"); + for (uint32_t I = 0; I < Size; ++I) + printTypeIndex("String", Indices[I], StreamIPI); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_STRING_ID (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, StringIdRecord &String, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("Id", String.getId(), StreamIPI); + W.printString("StringData", String.getString()); + }); + + if (LVScope *Namespace = Shared->NamespaceDeduction.get( + String.getString(), /*CheckScope=*/false)) { + // The function is already at different scope. In order to reflect + // the correct parent, move it to the namespace. + if (LVScope *Scope = Element->getParentScope()) + Scope->removeElement(Element); + Namespace->addElement(Element); + } + + return Error::success(); +} + +// LF_UDT_SRC_LINE (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + UdtSourceLineRecord &SourceLine, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("UDT", SourceLine.getUDT(), StreamIPI); + printTypeIndex("SourceFile", SourceLine.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", SourceLine.getLineNumber()); + }); + return Error::success(); +} + +// LF_UDT_MOD_SRC_LINE (TPI)/(IPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + UdtModSourceLineRecord &ModSourceLine, + TypeIndex TI, LVElement *Element) { + // All args are references into the TPI/IPI stream. + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamIPI); + printTypeIndex("\nTI", TI, StreamIPI); + printTypeIndex("UDT", ModSourceLine.getUDT(), StreamIPI); + printTypeIndex("SourceFile", ModSourceLine.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", ModSourceLine.getLineNumber()); + W.printNumber("Module", ModSourceLine.getModule()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_PRECOMP (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, PrecompRecord &Precomp, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printHex("StartIndex", Precomp.getStartTypeIndex()); + W.printHex("Count", Precomp.getTypesCount()); + W.printHex("Signature", Precomp.getSignature()); + W.printString("PrecompFile", Precomp.getPrecompFilePath()); + printTypeEnd(Record); + }); + return Error::success(); +} + +// LF_ENDPRECOMP (TPI) +Error LVLogicalVisitor::visitKnownRecord(CVType &Record, + EndPrecompRecord &EndPrecomp, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printTypeBegin(Record, TI, Element, StreamTPI); + W.printHex("Signature", EndPrecomp.getSignature()); + printTypeEnd(Record); + }); + return Error::success(); +} + +Error LVLogicalVisitor::visitUnknownMember(CVMemberRecord &Record, + TypeIndex TI) { + LLVM_DEBUG({ W.printHex("UnknownMember", unsigned(Record.Kind)); }); + return Error::success(); +} + +// LF_BCLASS, LF_BINTERFACE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + BaseClassRecord &Base, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("BaseType", Base.getBaseType(), StreamTPI); + W.printHex("BaseOffset", Base.getBaseOffset()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + LVElement *BaseClass = getElement(StreamTPI, Base.getBaseType()); + Symbol->setName(BaseClass->getName()); + Symbol->setType(BaseClass); + Symbol->setAccessibilityCode(Base.getAccess()); + static_cast(Element)->addElement(Symbol); + } + + return Error::success(); +} + +// LF_MEMBER +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + DataMemberRecord &Field, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Field.getType(), StreamTPI); + W.printHex("FieldOffset", Field.getFieldOffset()); + W.printString("Name", Field.getName()); + printMemberEnd(Record); + }); + + // Create the data member. + createDataMember(Record, static_cast(Element), Field.getName(), + Field.getType(), Field.getAccess()); + return Error::success(); +} + +// LF_ENUMERATE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + EnumeratorRecord &Enum, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + W.printNumber("EnumValue", Enum.getValue()); + W.printString("Name", Enum.getName()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVType *Type = CurrentType) { + Type->setName(Enum.getName()); + SmallString<16> Value; + Enum.getValue().toString(Value, 16, true, true); + Type->setValue(Value); + static_cast(Element)->addElement(CurrentType); + } + + return Error::success(); +} + +// LF_INDEX +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + ListContinuationRecord &Cont, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("ContinuationIndex", Cont.getContinuationIndex(), StreamTPI); + printMemberEnd(Record); + }); + return Error::success(); +} + +// LF_NESTTYPE +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + NestedTypeRecord &Nested, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Nested.getNestedType(), StreamTPI); + W.printString("Name", Nested.getName()); + printMemberEnd(Record); + }); + + if (LVElement *Typedef = createElement(SymbolKind::S_UDT)) { + Typedef->setName(Nested.getName()); + LVElement *NestedType = getElement(StreamTPI, Nested.getNestedType()); + Typedef->setType(NestedType); + LVScope *Scope = static_cast(Element); + Scope->addElement(Typedef); + + if (NestedType && NestedType->getIsNested()) { + // 'Element' is an aggregate type that may contains this nested type + // definition. Used their scoped names, to decide on their relationship. + StringRef RecordName = getRecordName(types(), TI); + + StringRef NestedTypeName = NestedType->getName(); + if (NestedTypeName.size() && RecordName.size()) { + StringRef OuterComponent; + std::tie(OuterComponent, std::ignore) = + getInnerComponent(NestedTypeName); + // We have an already created nested type. Add it to the current scope + // and update all its children if any. + if (OuterComponent.size() && OuterComponent.equals(RecordName)) { + if (!NestedType->getIsScopedAlready()) { + Scope->addElement(NestedType); + NestedType->setIsScopedAlready(); + NestedType->updateLevel(Scope); + } + Typedef->resetIncludeInPrint(); + } + } + } + } + + return Error::success(); +} + +// LF_ONEMETHOD +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + OneMethodRecord &Method, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Method.getType(), StreamTPI); + // If virtual, then read the vftable offset. + if (Method.isIntroducingVirtual()) + W.printHex("VFTableOffset", Method.getVFTableOffset()); + W.printString("Name", Method.getName()); + printMemberEnd(Record); + }); + + // All the LF_ONEMETHOD objects share the same type description. + // We have to create a scope object for each one and get the required + // information from the LF_MFUNCTION object. + ProcessArgumentList = true; + if (LVElement *MemberFunction = createElement(TypeLeafKind::LF_ONEMETHOD)) { + MemberFunction->setIsFinalized(); + static_cast(Element)->addElement(MemberFunction); + + MemberFunction->setName(Method.getName()); + MemberFunction->setAccessibilityCode(Method.getAccess()); + + MethodKind Kind = Method.getMethodKind(); + if (Kind == MethodKind::Static) + MemberFunction->setIsStatic(); + MemberFunction->setVirtualityCode(Kind); + + MethodOptions Flags = Method.Attrs.getFlags(); + if (MethodOptions::CompilerGenerated == + (Flags & MethodOptions::CompilerGenerated)) + MemberFunction->setIsArtificial(); + + LazyRandomTypeCollection &Types = types(); + CVType CVMethodType = Types.getType(Method.getType()); + if (Error Err = + finishVisitation(CVMethodType, Method.getType(), MemberFunction)) + return Err; + } + ProcessArgumentList = false; + + return Error::success(); +} + +// LF_METHOD +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + OverloadedMethodRecord &Method, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + W.printHex("MethodCount", Method.getNumOverloads()); + printTypeIndex("MethodListIndex", Method.getMethodList(), StreamTPI); + W.printString("Name", Method.getName()); + printMemberEnd(Record); + }); + + // Record the overloaded method name, which will be used during the + // traversal of the method list. + LazyRandomTypeCollection &Types = types(); + OverloadedMethodName = Method.getName(); + CVType CVMethods = Types.getType(Method.getMethodList()); + if (Error Err = finishVisitation(CVMethods, Method.getMethodList(), Element)) + return Err; + + return Error::success(); +} + +// LF_STMEMBER +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + StaticDataMemberRecord &Field, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", Field.getType(), StreamTPI); + W.printString("Name", Field.getName()); + printMemberEnd(Record); + }); + + // Create the data member. + createDataMember(Record, static_cast(Element), Field.getName(), + Field.getType(), Field.getAccess()); + return Error::success(); +} + +// LF_VFUNCTAB +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + VFPtrRecord &VFTable, TypeIndex TI, + LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("Type", VFTable.getType(), StreamTPI); + printMemberEnd(Record); + }); + return Error::success(); +} + +// LF_VBCLASS, LF_IVBCLASS +Error LVLogicalVisitor::visitKnownMember(CVMemberRecord &Record, + VirtualBaseClassRecord &Base, + TypeIndex TI, LVElement *Element) { + LLVM_DEBUG({ + printMemberBegin(Record, TI, Element, StreamTPI); + printTypeIndex("BaseType", Base.getBaseType(), StreamTPI); + printTypeIndex("VBPtrType", Base.getVBPtrType(), StreamTPI); + W.printHex("VBPtrOffset", Base.getVBPtrOffset()); + W.printHex("VBTableIndex", Base.getVTableIndex()); + printMemberEnd(Record); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + LVElement *BaseClass = getElement(StreamTPI, Base.getBaseType()); + Symbol->setName(BaseClass->getName()); + Symbol->setType(BaseClass); + Symbol->setAccessibilityCode(Base.getAccess()); + Symbol->setVirtualityCode(MethodKind::Virtual); + static_cast(Element)->addElement(Symbol); + } + + return Error::success(); +} + +Error LVLogicalVisitor::visitMemberRecord(CVMemberRecord &Record, + TypeVisitorCallbacks &Callbacks, + TypeIndex TI, LVElement *Element) { + if (Error Err = Callbacks.visitMemberBegin(Record)) + return Err; + + switch (Record.Kind) { + default: + if (Error Err = Callbacks.visitUnknownMember(Record)) + return Err; + break; +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (Error Err = \ + visitKnownMember(Record, Callbacks, TI, Element)) \ + return Err; \ + break; \ + } +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + MEMBER_RECORD(EnumVal, EnumVal, AliasName) +#define TYPE_RECORD(EnumName, EnumVal, Name) +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + } + + if (Error Err = Callbacks.visitMemberEnd(Record)) + return Err; + + return Error::success(); +} + +Error LVLogicalVisitor::finishVisitation(CVType &Record, TypeIndex TI, + LVElement *Element) { + switch (Record.kind()) { + default: + if (Error Err = visitUnknownType(Record, TI)) + return Err; + break; +#define TYPE_RECORD(EnumName, EnumVal, Name) \ + case EnumName: { \ + if (Error Err = visitKnownRecord(Record, TI, Element)) \ + return Err; \ + break; \ + } +#define TYPE_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) \ + TYPE_RECORD(EnumVal, EnumVal, AliasName) +#define MEMBER_RECORD(EnumName, EnumVal, Name) +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + } + + return Error::success(); +} + +// Customized version of 'FieldListVisitHelper'. +Error LVLogicalVisitor::visitFieldListMemberStream( + TypeIndex TI, LVElement *Element, ArrayRef FieldList) { + BinaryByteStream Stream(FieldList, llvm::support::little); + BinaryStreamReader Reader(Stream); + FieldListDeserializer Deserializer(Reader); + TypeVisitorCallbackPipeline Pipeline; + Pipeline.addCallbackToPipeline(Deserializer); + + TypeLeafKind Leaf; + while (!Reader.empty()) { + if (Error Err = Reader.readEnum(Leaf)) + return Err; + + CVMemberRecord Record; + Record.Kind = Leaf; + if (Error Err = visitMemberRecord(Record, Pipeline, TI, Element)) + return Err; + } + + return Error::success(); +} + +void LVLogicalVisitor::addElement(LVScope *Scope, bool IsCompileUnit) { + // The CodeView specifications does not treat S_COMPILE2 and S_COMPILE3 + // as symbols that open a scope. The CodeView reader, treat them in a + // similar way as DWARF. As there is no a symbole S_END to close the + // compile unit, we need to check for the next compile unit. + if (IsCompileUnit) { + if (!ScopeStack.empty()) + popScope(); + InCompileUnitScope = true; + } + + pushScope(Scope); + ReaderParent->addElement(Scope); +} + +void LVLogicalVisitor::addElement(LVSymbol *Symbol) { + ReaderScope->addElement(Symbol); +} + +void LVLogicalVisitor::addElement(LVType *Type) { + ReaderScope->addElement(Type); +} + +LVElement *LVLogicalVisitor::createElement(TypeLeafKind Kind) { + CurrentScope = nullptr; + CurrentSymbol = nullptr; + CurrentType = nullptr; + + if (Kind < TypeIndex::FirstNonSimpleIndex) { + CurrentType = new LVType(); + CurrentType->setIsBase(); + CurrentType->setTag(dwarf::DW_TAG_base_type); + if (options().getAttributeBase()) + CurrentType->setIncludeInPrint(); + return CurrentType; + } + + switch (Kind) { + // Types. + case TypeLeafKind::LF_ENUMERATE: + CurrentType = new LVTypeEnumerator(); + CurrentType->setTag(dwarf::DW_TAG_enumerator); + return CurrentType; + case TypeLeafKind::LF_MODIFIER: + CurrentType = new LVType(); + CurrentType->setIsModifier(); + return CurrentType; + case TypeLeafKind::LF_POINTER: + CurrentType = new LVType(); + CurrentType->setIsPointer(); + CurrentType->setName("*"); + CurrentType->setTag(dwarf::DW_TAG_pointer_type); + return CurrentType; + + // Symbols. + case TypeLeafKind::LF_BCLASS: + case TypeLeafKind::LF_IVBCLASS: + case TypeLeafKind::LF_VBCLASS: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setTag(dwarf::DW_TAG_inheritance); + CurrentSymbol->setIsInheritance(); + return CurrentSymbol; + case TypeLeafKind::LF_MEMBER: + case TypeLeafKind::LF_STMEMBER: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsMember(); + CurrentSymbol->setTag(dwarf::DW_TAG_member); + return CurrentSymbol; + + // Scopes. + case TypeLeafKind::LF_ARRAY: + CurrentScope = new LVScopeArray(); + CurrentScope->setTag(dwarf::DW_TAG_array_type); + return CurrentScope; + case TypeLeafKind::LF_CLASS: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setTag(dwarf::DW_TAG_class_type); + CurrentScope->setIsClass(); + return CurrentScope; + case TypeLeafKind::LF_ENUM: + CurrentScope = new LVScopeEnumeration(); + CurrentScope->setTag(dwarf::DW_TAG_enumeration_type); + return CurrentScope; + case TypeLeafKind::LF_METHOD: + case TypeLeafKind::LF_ONEMETHOD: + case TypeLeafKind::LF_PROCEDURE: + CurrentScope = new LVScopeFunction(); + CurrentScope->setIsSubprogram(); + CurrentScope->setTag(dwarf::DW_TAG_subprogram); + return CurrentScope; + case TypeLeafKind::LF_STRUCTURE: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setIsStructure(); + CurrentScope->setTag(dwarf::DW_TAG_structure_type); + return CurrentScope; + case TypeLeafKind::LF_UNION: + CurrentScope = new LVScopeAggregate(); + CurrentScope->setIsUnion(); + CurrentScope->setTag(dwarf::DW_TAG_union_type); + return CurrentScope; + default: + // If '--internal=tag' and '--print=warning' are specified in the command + // line, we record and print each seen 'TypeLeafKind'. + break; + } + return nullptr; +} + +LVElement *LVLogicalVisitor::createElement(SymbolKind Kind) { + CurrentScope = nullptr; + CurrentSymbol = nullptr; + CurrentType = nullptr; + switch (Kind) { + // Types. + case SymbolKind::S_UDT: + CurrentType = new LVTypeDefinition(); + CurrentType->setTag(dwarf::DW_TAG_typedef); + return CurrentType; + + // Symbols. + case SymbolKind::S_CONSTANT: + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsConstant(); + CurrentSymbol->setTag(dwarf::DW_TAG_constant); + return CurrentSymbol; + + case SymbolKind::S_BPREL32: + case SymbolKind::S_REGREL32: + case SymbolKind::S_GDATA32: + case SymbolKind::S_LDATA32: + case SymbolKind::S_LOCAL: + // During the symbol traversal more information is available to + // determine if the symbol is a parameter or a variable. At this + // stage mark it as variable. + CurrentSymbol = new LVSymbol(); + CurrentSymbol->setIsVariable(); + CurrentSymbol->setTag(dwarf::DW_TAG_variable); + return CurrentSymbol; + + // Scopes. + case SymbolKind::S_BLOCK32: + CurrentScope = new LVScope(); + CurrentScope->setIsLexicalBlock(); + CurrentScope->setTag(dwarf::DW_TAG_lexical_block); + return CurrentScope; + case SymbolKind::S_COMPILE2: + case SymbolKind::S_COMPILE3: + CurrentScope = new LVScopeCompileUnit(); + CurrentScope->setTag(dwarf::DW_TAG_compile_unit); + Reader->setCompileUnit(static_cast(CurrentScope)); + return CurrentScope; + case SymbolKind::S_INLINESITE: + case SymbolKind::S_INLINESITE2: + CurrentScope = new LVScopeFunctionInlined(); + CurrentScope->setIsInlinedFunction(); + CurrentScope->setTag(dwarf::DW_TAG_inlined_subroutine); + return CurrentScope; + case SymbolKind::S_LPROC32: + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32_ID: + case SymbolKind::S_GPROC32_ID: + case SymbolKind::S_SEPCODE: + case SymbolKind::S_THUNK32: + CurrentScope = new LVScopeFunction(); + CurrentScope->setIsSubprogram(); + CurrentScope->setTag(dwarf::DW_TAG_subprogram); + return CurrentScope; + default: + // If '--internal=tag' and '--print=warning' are specified in the command + // line, we record and print each seen 'SymbolKind'. + break; + } + return nullptr; +} + +LVElement *LVLogicalVisitor::createElement(TypeIndex TI, TypeLeafKind Kind) { + LVElement *Element = Shared->TypeRecords.find(StreamTPI, TI); + if (!Element) { + // We are dealing with a base type or pointer to a base type, which are + // not included explicitly in the CodeView format. + if (Kind < TypeIndex::FirstNonSimpleIndex) { + Element = createElement(Kind); + Element->setIsFinalized(); + Shared->TypeRecords.add(StreamTPI, (TypeIndex)Kind, Kind, Element); + Element->setOffset(Kind); + return Element; + } + // We are dealing with a pointer to a base type. + if (TI.getIndex() < TypeIndex::FirstNonSimpleIndex) { + Element = createElement(Kind); + Shared->TypeRecords.add(StreamTPI, TI, Kind, Element); + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + return Element; + } + + W.printString("** Not implemented. **"); + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeLeafKind", formatTypeLeafKind(Kind)); + return nullptr; + } + + Element->setOffset(TI.getIndex()); + Element->setOffsetFromTypeIndex(); + return Element; +} + +void LVLogicalVisitor::createDataMember(CVMemberRecord &Record, LVScope *Parent, + StringRef Name, TypeIndex TI, + MemberAccess Access) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeName", Name); + }); + + createElement(Record.Kind); + if (LVSymbol *Symbol = CurrentSymbol) { + Symbol->setName(Name); + if (TI.isNoneType() || TI.isSimple()) + Symbol->setType(getElement(StreamTPI, TI)); + else { + LazyRandomTypeCollection &Types = types(); + CVType CVMemberType = Types.getType(TI); + if (CVMemberType.kind() == LF_BITFIELD) { + if (Error Err = finishVisitation(CVMemberType, TI, Symbol)) { + consumeError(std::move(Err)); + return; + } + } else + Symbol->setType(getElement(StreamTPI, TI)); + } + Symbol->setAccessibilityCode(Access); + Parent->addElement(Symbol); + } +} + +LVSymbol *LVLogicalVisitor::createParameter(LVElement *Element, StringRef Name, + LVScope *Parent) { + LVSymbol *Parameter = new LVSymbol(); + Parent->addElement(Parameter); + Parameter->setIsParameter(); + Parameter->setTag(dwarf::DW_TAG_formal_parameter); + Parameter->setName(Name); + Parameter->setType(Element); + return Parameter; +} + +LVSymbol *LVLogicalVisitor::createParameter(TypeIndex TI, StringRef Name, + LVScope *Parent) { + return createParameter(getElement(StreamTPI, TI), Name, Parent); +} + +LVType *LVLogicalVisitor::createBaseType(TypeIndex TI, StringRef TypeName) { + TypeLeafKind SimpleKind = (TypeLeafKind)TI.getSimpleKind(); + TypeIndex TIR = (TypeIndex)SimpleKind; + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TIR, StreamTPI); + W.printString("TypeName", TypeName); + }); + + if (LVElement *Element = Shared->TypeRecords.find(StreamTPI, TIR)) + return static_cast(Element); + + if (createElement(TIR, SimpleKind)) { + CurrentType->setName(TypeName); + Reader->getCompileUnit()->addElement(CurrentType); + } + return CurrentType; +} + +LVType *LVLogicalVisitor::createPointerType(TypeIndex TI, StringRef TypeName) { + LLVM_DEBUG({ + printTypeIndex("TypeIndex", TI, StreamTPI); + W.printString("TypeName", TypeName); + }); + + if (LVElement *Element = Shared->TypeRecords.find(StreamTPI, TI)) + return static_cast(Element); + + LVType *Pointee = createBaseType(TI, TypeName.drop_back(1)); + if (createElement(TI, TypeLeafKind::LF_POINTER)) { + CurrentType->setIsFinalized(); + CurrentType->setType(Pointee); + Reader->getCompileUnit()->addElement(CurrentType); + } + return CurrentType; +} + +void LVLogicalVisitor::createParents(StringRef ScopedName, LVElement *Element) { + // For the given test case: + // + // struct S { enum E { ... }; }; + // S::E V; + // + // 0 | S_LOCAL `V` + // type=0x1004 (S::E), flags = none + // 0x1004 | LF_ENUM `S::E` + // options: has unique name | is nested + // 0x1009 | LF_STRUCTURE `S` + // options: contains nested class + // + // When the local 'V' is processed, its type 'E' is created. But There is + // no direct reference to its parent 'S'. We use the scoped name for 'E', + // to create its parents. + + // The input scoped name must have at least parent and nested names. + // Drop the last element name, as it corresponds to the nested type. + LVStringRefs Components = getAllLexicalComponents(ScopedName); + if (Components.size() < 2) + return; + Components.pop_back(); + + LVStringRefs::size_type FirstNamespace; + LVStringRefs::size_type FirstAggregate; + std::tie(FirstNamespace, FirstAggregate) = + Shared->NamespaceDeduction.find(Components); + + LLVM_DEBUG({ + W.printString("First Namespace", Components[FirstNamespace]); + W.printString("First NonNamespace", Components[FirstAggregate]); + }); + + // Create any referenced namespaces. + if (FirstNamespace < FirstAggregate) { + Shared->NamespaceDeduction.get( + LVStringRefs(Components.begin() + FirstNamespace, + Components.begin() + FirstAggregate)); + } + + // Traverse the enclosing scopes (aggregates) and create them. In the + // case of nested empty aggregates, MSVC does not emit a full record + // description. It emits only the reference record. + LVScope *Aggregate = nullptr; + TypeIndex TIAggregate; + std::string AggregateName = getScopedName( + LVStringRefs(Components.begin(), Components.begin() + FirstAggregate)); + + // This traversal is executed at least once. + for (LVStringRefs::size_type Index = FirstAggregate; + Index < Components.size(); ++Index) { + AggregateName = getScopedName(LVStringRefs(Components.begin() + Index, + Components.begin() + Index + 1), + AggregateName); + TIAggregate = Shared->ForwardReferences.remap( + Shared->TypeRecords.find(StreamTPI, AggregateName)); + Aggregate = + TIAggregate.isNoneType() + ? nullptr + : static_cast(getElement(StreamTPI, TIAggregate)); + } + + // Workaround for cases where LF_NESTTYPE is missing for nested templates. + // If we manage to get parent information from the scoped name, we can add + // the nested type without relying on the LF_NESTTYPE. + if (Aggregate && !Element->getIsScopedAlready()) { + Aggregate->addElement(Element); + Element->setIsScopedAlready(); + } +} + +LVElement *LVLogicalVisitor::getElement(uint32_t StreamIdx, TypeIndex TI, + LVScope *Parent) { + LLVM_DEBUG({ printTypeIndex("TypeIndex", TI, StreamTPI); }); + TI = Shared->ForwardReferences.remap(TI); + LLVM_DEBUG({ printTypeIndex("TypeIndex Remap", TI, StreamTPI); }); + + LVElement *Element = Shared->TypeRecords.find(StreamIdx, TI); + if (!Element) { + if (TI.isNoneType() || TI.isSimple()) { + StringRef TypeName = TypeIndex::simpleTypeName(TI); + // If the name ends with "*", create 2 logical types: a pointer and a + // pointee type. TypeIndex is composed of a SympleTypeMode byte followed + // by a SimpleTypeKind byte. The logical pointer will be identified by + // the full TypeIndex value and the pointee by the SimpleTypeKind. + return (TypeName.back() == '*') ? createPointerType(TI, TypeName) + : createBaseType(TI, TypeName); + } + + LLVM_DEBUG({ W.printHex("TypeIndex not implemented: ", TI.getIndex()); }); + return nullptr; + } + + // The element has been finalized. + if (Element->getIsFinalized()) + return Element; + + // Add the element in case of a given parent. + if (Parent) + Parent->addElement(Element); + + // Check for a composite type. + LazyRandomTypeCollection &Types = types(); + CVType CVRecord = Types.getType(TI); + if (Error Err = finishVisitation(CVRecord, TI, Element)) { + consumeError(std::move(Err)); + return nullptr; + } + Element->setIsFinalized(); + return Element; +} + +void LVLogicalVisitor::processLines() { + // Traverse the collected LF_UDT_SRC_LINE records and add the source line + // information to the logical elements. + for (const TypeIndex &Entry : Shared->LineRecords) { + CVType CVRecord = ids().getType(Entry); + UdtSourceLineRecord Line; + if (Error Err = TypeDeserializer::deserializeAs( + const_cast(CVRecord), Line)) + consumeError(std::move(Err)); + else { + LLVM_DEBUG({ + printTypeIndex("UDT", Line.getUDT(), StreamIPI); + printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI); + W.printNumber("LineNumber", Line.getLineNumber()); + }); + + if (LVElement *Element = + Shared->TypeRecords.find(StreamTPI, Line.getUDT())) { + Element->setLineNumber(Line.getLineNumber()); + Element->setFilenameIndex( + Shared->StringRecords.findIndex(Line.getSourceFile())); + } + } + } +} + +void LVLogicalVisitor::processNamespaces() { + // Create namespaces. + Shared->NamespaceDeduction.init(); +} + +void LVLogicalVisitor::processFiles() { Shared->StringRecords.addFilenames(); } + +void LVLogicalVisitor::printRecords(raw_ostream &OS) const { + if (!options().getInternalTag()) + return; + + unsigned Count = 0; + auto PrintItem = [&](StringRef Name) { + auto NewLine = [&]() { + if (++Count == 4) { + Count = 0; + OS << "\n"; + } + }; + std::string TheName(Name); + OS << format("%20s", TheName.c_str()); + NewLine(); + }; + + OS << "\nTypes:\n"; + for (const TypeLeafKind &Kind : Shared->TypeKinds) + PrintItem(formatTypeLeafKind(Kind)); + Shared->TypeKinds.clear(); + + Count = 0; + OS << "\nSymbols:\n"; + for (const SymbolKind &Kind : Shared->SymbolKinds) + PrintItem(LVCodeViewReader::getSymbolKindName(Kind)); + Shared->SymbolKinds.clear(); + + OS << "\n"; +} + +Error LVLogicalVisitor::inlineSiteAnnotation(LVScope *AbstractFunction, + LVScope *InlinedFunction, + InlineSiteSym &InlineSite) { + // Get the parent scope to update the address ranges of the nested + // scope representing the inlined function. + LVAddress ParentLowPC = 0; + LVScope *Parent = InlinedFunction->getParentScope(); + if (const LVLocations *Locations = Parent->getRanges()) { + for (const LVLocation *Location : *Locations) { + ParentLowPC = Location->getLowerAddress(); + break; + } + } + + // For the given inlinesite, get the initial line number and it's + // source filename. Update the logical scope representing it. + uint32_t LineNumber = 0; + StringRef Filename; + LVInlineeInfo::iterator Iter = InlineeInfo.find(InlineSite.Inlinee); + if (Iter != InlineeInfo.end()) { + LineNumber = Iter->second.first; + Filename = Iter->second.second; + AbstractFunction->setLineNumber(LineNumber); + // TODO: This part needs additional work in order to set properly the + // correct filename in order to detect changes between filenames. + // AbstractFunction->setFilename(Filename); + } + + LLVM_DEBUG({ + dbgs() << "inlineSiteAnnotation\n" + << "Abstract: " << AbstractFunction->getName() << "\n" + << "Inlined: " << InlinedFunction->getName() << "\n" + << "Parent: " << Parent->getName() << "\n" + << "Low PC: " << hexValue(ParentLowPC) << "\n"; + }); + + // Limitation: Currently we don't track changes in the FileOffset. The + // side effects are the caller that it is unable to differentiate the + // source filename for the inlined code. + uint64_t CodeOffset = ParentLowPC; + int32_t LineOffset = LineNumber; + uint32_t FileOffset = 0; + + auto UpdateClose = [&]() { LLVM_DEBUG({ dbgs() << ("\n"); }); }; + auto UpdateCodeOffset = [&](uint32_t Delta) { + CodeOffset += Delta; + LLVM_DEBUG({ + dbgs() << formatv(" code 0x{0} (+0x{1})", utohexstr(CodeOffset), + utohexstr(Delta)); + }); + }; + auto UpdateLineOffset = [&](int32_t Delta) { + LineOffset += Delta; + LLVM_DEBUG({ + char Sign = Delta > 0 ? '+' : '-'; + dbgs() << formatv(" line {0} ({1}{2})", LineOffset, Sign, + std::abs(Delta)); + }); + }; + auto UpdateFileOffset = [&](int32_t Offset) { + FileOffset = Offset; + LLVM_DEBUG({ dbgs() << formatv(" file {0}", FileOffset); }); + }; + + LVLines InlineeLines; + auto CreateLine = [&]() { + // Create the logical line record. + LVLineDebug *Line = new LVLineDebug(); + Line->setAddress(CodeOffset); + Line->setLineNumber(LineOffset); + // TODO: This part needs additional work in order to set properly the + // correct filename in order to detect changes between filenames. + // Line->setFilename(Filename); + InlineeLines.push_back(Line); + }; + + bool SeenLowAddress = false; + bool SeenHighAddress = false; + uint64_t LowPC = 0; + uint64_t HighPC = 0; + + for (auto &Annot : InlineSite.annotations()) { + LLVM_DEBUG({ + dbgs() << formatv(" {0}", + fmt_align(toHex(Annot.Bytes), AlignStyle::Left, 9)); + }); + + // Use the opcode to interpret the integer values. + switch (Annot.OpCode) { + case BinaryAnnotationsOpCode::ChangeCodeOffset: + case BinaryAnnotationsOpCode::CodeOffset: + case BinaryAnnotationsOpCode::ChangeCodeLength: + UpdateCodeOffset(Annot.U1); + UpdateClose(); + if (Annot.OpCode == BinaryAnnotationsOpCode::ChangeCodeOffset) { + CreateLine(); + LowPC = CodeOffset; + SeenLowAddress = true; + break; + } + if (Annot.OpCode == BinaryAnnotationsOpCode::ChangeCodeLength) { + HighPC = CodeOffset - 1; + SeenHighAddress = true; + break; + } + break; + case BinaryAnnotationsOpCode::ChangeCodeLengthAndCodeOffset: + UpdateCodeOffset(Annot.U2); + UpdateClose(); + break; + case BinaryAnnotationsOpCode::ChangeLineOffset: + case BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset: + UpdateCodeOffset(Annot.U1); + UpdateLineOffset(Annot.S1); + UpdateClose(); + if (Annot.OpCode == + BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset) + CreateLine(); + break; + case BinaryAnnotationsOpCode::ChangeFile: + UpdateFileOffset(Annot.U1); + UpdateClose(); + break; + default: + break; + } + if (SeenLowAddress && SeenHighAddress) { + SeenLowAddress = false; + SeenHighAddress = false; + InlinedFunction->addObject(LowPC, HighPC); + } + } + + Reader->addInlineeLines(InlinedFunction, InlineeLines); + UpdateClose(); + + return Error::success(); +} Index: llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp =================================================================== --- llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp +++ llvm/lib/DebugInfo/PDB/Native/DbiModuleList.cpp @@ -240,7 +240,9 @@ } uint32_t DbiModuleList::getModuleCount() const { - return FileInfoHeader->NumModules; + // Workaround to avoid the crash until upstream issue is fixed: + // https://github.com/llvm/llvm-project/issues/55214 + return FileInfoHeader ? FileInfoHeader->NumModules : 0; } uint32_t DbiModuleList::getSourceFileCount() const { Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-compare-logical-elements.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-compare-logical-elements.test @@ -0,0 +1,101 @@ +; Test case 1 - General options + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Compare mode - Logical view. +; The output shows in view form the 'missing (-), added (+)' elements, +; giving more context by swapping the reference and target object files. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=view \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/test-codeview-clang.o \ +; RUN: %p/Inputs/test-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Reference: 'test-codeview-clang.o' +; ONE-NEXT: Target: 'test-codeview-msvc.o' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'test-codeview-clang.o' +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [002] {TypeAlias} 'INTPTR' -> '* const int' +; ONE-NEXT: [002] {Function} extern not_inlined 'foo' -> 'int' +; ONE-NEXT: -[003] {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [003] {Parameter} 'ParamBool' -> 'bool' +; ONE-NEXT: [003] {Parameter} 'ParamPtr' -> '* const int' +; ONE-NEXT: [003] {Parameter} 'ParamUnsigned' -> 'unsigned' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Variable} 'CONSTANT' -> 'const int' +; ONE-NEXT: +[004] {TypeAlias} 'INTEGER' -> 'int' + +; Compare mode - Logical elements. +; The output shows in tabular form the 'missing (-), added (+)' elements, +; giving more context by swapping the reference and target object files. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=list \ +; RUN: --print=symbols,types,summary \ +; RUN: %p/Inputs/test-codeview-clang.o \ +; RUN: %p/Inputs/test-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Reference: 'test-codeview-clang.o' +; TWO-NEXT: Target: 'test-codeview-msvc.o' +; TWO-EMPTY: +; TWO-NEXT: (1) Missing Types: +; TWO-NEXT: -[003] {TypeAlias} 'INTEGER' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: (1) Added Types: +; TWO-NEXT: +[004] {TypeAlias} 'INTEGER' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Element Expected Missing Added +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Scopes 4 0 0 +; TWO-NEXT: Symbols 0 0 0 +; TWO-NEXT: Types 2 1 1 +; TWO-NEXT: Lines 0 0 0 +; TWO-NEXT: ---------------------------------------- +; TWO-NEXT: Total 6 1 1 + +; Changing the 'Reference' and 'Target' order: + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --compare=types \ +; RUN: --report=list \ +; RUN: --print=symbols,types,summary \ +; RUN: %p/Inputs/test-codeview-msvc.o \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=THR %s + +; THR: Reference: 'test-codeview-msvc.o' +; THR-NEXT: Target: 'test-codeview-clang.o' +; THR-EMPTY: +; THR-NEXT: (1) Missing Types: +; THR-NEXT: -[004] {TypeAlias} 'INTEGER' -> 'int' +; THR-EMPTY: +; THR-NEXT: (1) Added Types: +; THR-NEXT: +[003] {TypeAlias} 'INTEGER' -> 'int' +; THR-EMPTY: +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Element Expected Missing Added +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Scopes 4 0 0 +; THR-NEXT: Symbols 0 0 0 +; THR-NEXT: Types 2 1 1 +; THR-NEXT: Lines 0 0 0 +; THR-NEXT: ---------------------------------------- +; THR-NEXT: Total 6 1 1 Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test @@ -0,0 +1,63 @@ +; Test case 1 - General options. + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Print basic details. +; The following command prints basic details for all the logical elements +; sorted by the debug information internal offset; it includes its lexical +; level and debug info format. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=offset \ +; RUN: --print=scopes,symbols,types,lines,instructions \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=offset \ +; RUN: --print=elements \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'test-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [002] {Function} extern not_inlined 'foo' -> 'int' +; ONE-NEXT: [003] {Parameter} 'ParamPtr' -> '* const int' +; ONE-NEXT: [003] {Parameter} 'ParamUnsigned' -> 'unsigned' +; ONE-NEXT: [003] {Parameter} 'ParamBool' -> 'bool' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Variable} 'CONSTANT' -> 'const int' +; ONE-NEXT: [004] 5 {Line} +; ONE-NEXT: [004] {Code} 'movl $0x7, 0x4(%rsp)' +; ONE-NEXT: [004] 6 {Line} +; ONE-NEXT: [004] {Code} 'movl $0x7, 0x1c(%rsp)' +; ONE-NEXT: [004] {Code} 'jmp 0x8' +; ONE-NEXT: [003] {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [003] 2 {Line} +; ONE-NEXT: [003] {Code} 'subq $0x20, %rsp' +; ONE-NEXT: [003] {Code} 'andb $0x1, %r8b' +; ONE-NEXT: [003] {Code} 'movb %r8b, 0x1b(%rsp)' +; ONE-NEXT: [003] {Code} 'movl %edx, 0x14(%rsp)' +; ONE-NEXT: [003] {Code} 'movq %rcx, 0x8(%rsp)' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] {Code} 'testb $0x1, 0x1b(%rsp)' +; ONE-NEXT: [003] {Code} 'je 0x15' +; ONE-NEXT: [003] 8 {Line} +; ONE-NEXT: [003] {Code} 'movl 0x14(%rsp), %eax' +; ONE-NEXT: [003] {Code} 'movl %eax, 0x1c(%rsp)' +; ONE-NEXT: [003] 9 {Line} +; ONE-NEXT: [003] {Code} 'movl 0x1c(%rsp), %eax' +; ONE-NEXT: [003] {Code} 'addq $0x20, %rsp' +; ONE-NEXT: [003] {Code} 'retq' +; ONE-NEXT: [002] {TypeAlias} 'INTPTR' -> '* const int' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-select-logical-elements.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-select-logical-elements.test @@ -0,0 +1,68 @@ +; Test case 1 - General options + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Select logical elements. +; The following prints all 'instructions', 'symbols' and 'types' that +; contain 'inte' or 'movl' in their names or types, using a tab layout +; and given the number of matches. + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --select-nocase --select-regex \ +; RUN: --select=INTe --select=movl \ +; RUN: --report=list \ +; RUN: --print=symbols,types,instructions,summary \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'test-codeview-clang.o' +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [003] {TypeAlias} 'INTEGER' -> 'int' +; ONE-NEXT: [004] {Code} 'movl $0x7, 0x1c(%rsp)' +; ONE-NEXT: [004] {Code} 'movl $0x7, 0x4(%rsp)' +; ONE-NEXT: [003] {Code} 'movl %eax, 0x1c(%rsp)' +; ONE-NEXT: [003] {Code} 'movl %edx, 0x14(%rsp)' +; ONE-NEXT: [003] {Code} 'movl 0x14(%rsp), %eax' +; ONE-NEXT: [003] {Code} 'movl 0x1c(%rsp), %eax' +; ONE-EMPTY: +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Element Total Printed +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Scopes 3 0 +; ONE-NEXT: Symbols 4 0 +; ONE-NEXT: Types 2 1 +; ONE-NEXT: Lines 21 6 +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Total 30 7 + +; RUN: llvm-debuginfo-analyzer --attribute=level \ +; RUN: --select-regex --select-nocase \ +; RUN: --select=INTe \ +; RUN: --report=list \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/test-codeview-clang.o \ +; RUN: %p/Inputs/test-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'test-codeview-clang.o' +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'test.cpp' +; TWO-NEXT: [003] {TypeAlias} 'INTEGER' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'test-codeview-msvc.o' +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'test.cpp' +; TWO-NEXT: [004] {TypeAlias} 'INTEGER' -> 'int' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/02-coff-logical-lines.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/02-coff-logical-lines.test @@ -0,0 +1,54 @@ +; Test case 2 - Assembler instructions. + +; hello-world.cpp +; 1 extern int printf(const char * format, ... ); +; 2 +; 3 int main() +; 4 { +; 5 printf("Hello, World\n"); +; 6 return 0; +; 7 } + +; Logical lines. +; The logical views shows the intermixed lines and assembler instructions, +; allowing to compare the code generated by the different toolchains. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --print=lines,instructions \ +; RUN: %p/Inputs/hello-world-codeview-clang.o \ +; RUN: %p/Inputs/hello-world-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'hello-world-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'hello-world.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] {Function} extern not_inlined 'main' -> 'int' +; ONE-NEXT: [003] 4 {Line} +; ONE-NEXT: [003] {Code} 'subq $0x28, %rsp' +; ONE-NEXT: [003] {Code} 'movl $0x0, 0x24(%rsp)' +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] {Code} 'leaq (%rip), %rcx' +; ONE-NEXT: [003] {Code} 'callq 0x0' +; ONE-NEXT: [003] 6 {Line} +; ONE-NEXT: [003] {Code} 'xorl %eax, %eax' +; ONE-NEXT: [003] {Code} 'addq $0x28, %rsp' +; ONE-NEXT: [003] {Code} 'retq' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'hello-world-codeview-msvc.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'hello-world.cpp' +; ONE-NEXT: [002] {Producer} 'Microsoft (R) Optimizing Compiler' +; ONE-NEXT: [002] {Function} extern not_inlined 'main' -> 'int' +; ONE-NEXT: [003] 4 {Line} +; ONE-NEXT: [003] {Code} 'subq $0x28, %rsp' +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] {Code} 'leaq (%rip), %rcx' +; ONE-NEXT: [003] {Code} 'callq 0x0' +; ONE-NEXT: [003] 6 {Line} +; ONE-NEXT: [003] {Code} 'xorl %eax, %eax' +; ONE-NEXT: [003] 7 {Line} +; ONE-NEXT: [003] {Code} 'addq $0x28, %rsp' +; ONE-NEXT: [003] {Code} 'retq' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/03-coff-incorrect-lexical-scope-typedef.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/03-coff-incorrect-lexical-scope-typedef.test @@ -0,0 +1,105 @@ +; Test case 3 - Incorrect lexical scope for typedef. + +; pr-44884.cpp +; 1 int bar(float Input) { return (int)Input; } +; 2 +; 3 unsigned foo(char Param) { +; 4 typedef int INT; // ** Definition for INT ** +; 5 INT Value = Param; +; 6 { +; 7 typedef float FLOAT; // ** Definition for FLOAT ** +; 8 { +; 9 FLOAT Added = Value + Param; +; 10 Value = bar(Added); +; 11 } +; 12 } +; 13 return Value + Param; +; 14 } + +; The lines 4 and 7 contains 2 typedefs, defined at different lexical +; scopes. + +; The above test is used to illustrates a scope issue found in the +; Clang compiler. +; PR44884: https://bugs.llvm.org/show_bug.cgi?id=44884 +; PR44229: https://github.com/llvm/llvm-project/issues/44229 + +; In the following logical views, we can see that the Clang compiler +; emits both typedefs at the same lexical scope (3), which is wrong. +; GCC and MSVC emit correct lexical scope for both typedefs. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=kind \ +; RUN: --print=symbols,types,lines \ +; RUN: %p/Inputs/pr-44884-codeview-clang.o \ +; RUN: %p/Inputs/pr-44884-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] {Function} extern not_inlined 'bar' -> 'int' +; ONE-NEXT: [003] {Parameter} 'Input' -> 'float' +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [002] {Function} extern not_inlined 'foo' -> 'unsigned' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Variable} 'Added' -> 'float' +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 10 {Line} +; ONE-NEXT: [003] {Parameter} 'Param' -> 'char' +; ONE-NEXT: [003] {TypeAlias} 'FLOAT' -> 'float' +; ONE-NEXT: [003] {TypeAlias} 'INT' -> 'int' +; ONE-NEXT: [003] {Variable} 'Value' -> 'int' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-44884-codeview-msvc.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; ONE-NEXT: [002] {Producer} 'Microsoft (R) Optimizing Compiler' +; ONE-NEXT: [002] {Function} extern not_inlined 'bar' -> 'int' +; ONE-NEXT: [003] {Variable} 'Input' -> 'float' +; ONE-NEXT: [003] 1 {Line} +; ONE-NEXT: [002] {Function} extern not_inlined 'foo' -> 'unsigned' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Block} +; ONE-NEXT: [005] {Variable} 'Added' -> 'float' +; ONE-NEXT: [004] {TypeAlias} 'FLOAT' -> 'float' +; ONE-NEXT: [004] 9 {Line} +; ONE-NEXT: [004] 10 {Line} +; ONE-NEXT: [003] {TypeAlias} 'INT' -> 'int' +; ONE-NEXT: [003] {Variable} 'Param' -> 'char' +; ONE-NEXT: [003] {Variable} 'Value' -> 'int' +; ONE-NEXT: [003] 3 {Line} +; ONE-NEXT: [003] 5 {Line} +; ONE-NEXT: [003] 13 {Line} +; ONE-NEXT: [003] 14 {Line} + +; Using the selection facilities, we can produce a simple tabular +; output showing just the logical types that are 'Typedef'. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Typedef \ +; RUN: --report=list \ +; RUN: --print=types \ +; RUN: %p/Inputs/pr-44884-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-44884-codeview-clang.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; TWO-NEXT: [003] {TypeAlias} 'FLOAT' -> 'float' +; TWO-NEXT: [003] {TypeAlias} 'INT' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-44884-codeview-msvc.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-44884.cpp' +; TWO-NEXT: [004] {TypeAlias} 'FLOAT' -> 'float' +; TWO-NEXT: [003] {TypeAlias} 'INT' -> 'int' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/04-coff-missing-nested-enumerators.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/04-coff-missing-nested-enumerators.test @@ -0,0 +1,135 @@ +; Test case 4 - Missing nested enumerations. + +; pr-46466.cpp +; 1 struct Struct { +; 2 union Union { +; 3 enum NestedEnum { RED, BLUE }; +; 4 }; +; 5 Union U; +; 6 }; +; 7 +; 8 Struct S; +; 9 int test() { +; 10 return S.U.BLUE; +; 11 } + +; The above test is used to illustrate a scope issue found in the Clang +; compiler. +; PR46466: https://bugs.llvm.org/show_bug.cgi?id=46466 +; PR45811: https://github.com/llvm/llvm-project/issues/45811 + +; In the following logical views, we can see that the DWARF debug +; information generated by the Clang compiler does not include any +; references to the enumerators 'RED' and 'BLUE'. The DWARF generated +; by GCC, CodeView generated by Clang and MSVC, it does include such +; references. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=name \ +; RUN: --print=symbols,types \ +; RUN: %p/Inputs/pr-46466-codeview-clang.o \ +; RUN: %p/Inputs/pr-46466-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] {Variable} extern 'S' -> 'Struct' +; ONE-NEXT: [002] 1 {Struct} 'Struct' +; ONE-NEXT: [003] {Member} public 'U' -> 'Union' +; ONE-NEXT: [003] 2 {Union} 'Union' +; ONE-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'int' +; ONE-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; ONE-NEXT: [005] {Enumerator} 'RED' = '0x0' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; ONE-NEXT: [002] {Producer} 'Microsoft (R) Optimizing Compiler' +; ONE-NEXT: [002] {Variable} extern 'S' -> 'Struct' +; ONE-NEXT: [002] 1 {Struct} 'Struct' +; ONE-NEXT: [003] {Member} public 'U' -> 'Union' +; ONE-NEXT: [003] 2 {Union} 'Union' +; ONE-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'int' +; ONE-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; ONE-NEXT: [005] {Enumerator} 'RED' = '0x0' + +; Using the selection facilities, we can produce a logical view +; showing just the logical types that are 'Enumerator' and its +; parents. The logical view is sorted by the types name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Enumerator \ +; RUN: --report=parents \ +; RUN: --print=types \ +; RUN: %p/Inputs/pr-46466-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; TWO-NEXT: [002] 1 {Struct} 'Struct' +; TWO-NEXT: [003] 2 {Union} 'Union' +; TWO-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'int' +; TWO-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; TWO-NEXT: [005] {Enumerator} 'RED' = '0x0' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; TWO-NEXT: [002] 1 {Struct} 'Struct' +; TWO-NEXT: [003] 2 {Union} 'Union' +; TWO-NEXT: [004] 3 {Enumeration} 'NestedEnum' -> 'int' +; TWO-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; TWO-NEXT: [005] {Enumerator} 'RED' = '0x0' + +; Using the selection facilities, we can produce a simple tabular output +; including a summary for the logical types that are 'Enumerator'. The +; logical view is sorted by the types name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-types=Enumerator \ +; RUN: --print=types,summary \ +; RUN: %p/Inputs/pr-46466-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=THR %s + +; THR: Logical View: +; THR-NEXT: [000] {File} 'pr-46466-codeview-clang.o' -> COFF-x86-64 +; THR-EMPTY: +; THR-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; THR-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; THR-NEXT: [005] {Enumerator} 'RED' = '0x0' +; THR-EMPTY: +; THR-NEXT: ----------------------------- +; THR-NEXT: Element Total Printed +; THR-NEXT: ----------------------------- +; THR-NEXT: Scopes 5 0 +; THR-NEXT: Symbols 2 0 +; THR-NEXT: Types 6 2 +; THR-NEXT: Lines 0 0 +; THR-NEXT: ----------------------------- +; THR-NEXT: Total 13 2 +; THR-EMPTY: +; THR-NEXT: Logical View: +; THR-NEXT: [000] {File} 'pr-46466-codeview-msvc.o' -> COFF-x86-64 +; THR-EMPTY: +; THR-NEXT: [001] {CompileUnit} 'pr-46466.cpp' +; THR-NEXT: [005] {Enumerator} 'BLUE' = '0x1' +; THR-NEXT: [005] {Enumerator} 'RED' = '0x0' +; THR-EMPTY: +; THR-NEXT: ----------------------------- +; THR-NEXT: Element Total Printed +; THR-NEXT: ----------------------------- +; THR-NEXT: Scopes 5 0 +; THR-NEXT: Symbols 2 0 +; THR-NEXT: Types 7 2 +; THR-NEXT: Lines 0 0 +; THR-NEXT: ----------------------------- +; THR-NEXT: Total 14 2 Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/05-coff-incorrect-lexical-scope-variable.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/05-coff-incorrect-lexical-scope-variable.test @@ -0,0 +1,95 @@ +; Test case 5 - Incorrect lexical scope variable. + +; pr-43860.cpp +; 1 #include "definitions.h" +; 2 forceinline int InlineFunction(int Param) { +; 3 int Var_1 = Param; +; 4 { +; 5 int Var_2 = Param + Var_1; +; 6 Var_1 = Var_2; +; 7 } +; 8 return Var_1; +; 9 } +; 10 +; 11 int test(int Param_1, int Param_2) { +; 12 int A = Param_1; +; 13 A += InlineFunction(Param_2); +; 14 return A; +; 15 } + +; The above test is used to illustrate a variable issue found in the +; Clang compiler. +; PR43860: https://bugs.llvm.org/show_bug.cgi?id=43860 +; PR43205: https://github.com/llvm/llvm-project/issues/43205 + +; In the following logical views, we can see that the CodeView debug +; information generated by the Clang compiler shows the variables +; 'Var_1' and 'Var_2' are at the same lexical scope (4) in the function +; 'InlineFuction'. +; The DWARF generated by GCC/Clang and CodeView generated by MSVC, show +; those variables at the correct lexical scope: '3' and '4' respectively. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format,producer \ +; RUN: --output-sort=name \ +; RUN: --print=symbols \ +; RUN: %p/Inputs/pr-43860-codeview-clang.o \ +; RUN: %p/Inputs/pr-43860-codeview-msvc.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [000] {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; ONE-NEXT: [002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: [002] 2 {Function} inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [003] {Parameter} '' -> 'int' +; ONE-NEXT: [002] {Function} extern not_inlined 'test' -> 'int' +; ONE-NEXT: [003] {Variable} 'A' -> 'int' +; ONE-NEXT: [003] {InlinedFunction} inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [004] {Parameter} 'Param' -> 'int' +; ONE-NEXT: [004] {Variable} 'Var_1' -> 'int' +; ONE-NEXT: [004] {Variable} 'Var_2' -> 'int' +; ONE-NEXT: [003] {Parameter} 'Param_1' -> 'int' +; ONE-NEXT: [003] {Parameter} 'Param_2' -> 'int' +; ONE-EMPTY: +; ONE-NEXT: Logical View: +; ONE-NEXT: [000] {File} 'pr-43860-codeview-msvc.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; ONE-NEXT: [002] {Producer} 'Microsoft (R) Optimizing Compiler' +; ONE-NEXT: [002] {Function} extern declared_inlined 'InlineFunction' -> 'int' +; ONE-NEXT: [003] {Block} +; ONE-NEXT: [004] {Variable} 'Var_2' -> 'int' +; ONE-NEXT: [003] {Variable} 'Param' -> 'int' +; ONE-NEXT: [003] {Variable} 'Var_1' -> 'int' +; ONE-NEXT: [002] {Function} extern not_inlined 'test' -> 'int' +; ONE-NEXT: [003] {Variable} 'A' -> 'int' +; ONE-NEXT: [003] {Variable} 'Param_1' -> 'int' +; ONE-NEXT: [003] {Variable} 'Param_2' -> 'int' + +; Using the selection facilities, we can produce a simple tabular output +; showing just the logical elements that have in their name the 'var' +; pattern. The logical view is sorted by the variables name. + +; RUN: llvm-debuginfo-analyzer --attribute=level,format \ +; RUN: --output-sort=name \ +; RUN: --select-regex --select-nocase \ +; RUN: --select=Var \ +; RUN: --report=list \ +; RUN: --print=symbols \ +; RUN: %p/Inputs/pr-43860-*.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=TWO %s + +; TWO: Logical View: +; TWO-NEXT: [000] {File} 'pr-43860-codeview-clang.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; TWO-NEXT: [004] {Variable} 'Var_1' -> 'int' +; TWO-NEXT: [004] {Variable} 'Var_2' -> 'int' +; TWO-EMPTY: +; TWO-NEXT: Logical View: +; TWO-NEXT: [000] {File} 'pr-43860-codeview-msvc.o' -> COFF-x86-64 +; TWO-EMPTY: +; TWO-NEXT: [001] {CompileUnit} 'pr-43860.cpp' +; TWO-NEXT: [003] {Variable} 'Var_1' -> 'int' +; TWO-NEXT: [004] {Variable} 'Var_2' -> 'int' Index: llvm/test/tools/llvm-debuginfo-analyzer/COFF/06-coff-full-logical-view.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-debuginfo-analyzer/COFF/06-coff-full-logical-view.test @@ -0,0 +1,97 @@ +; Test case 6 - Full logical view + +; test.cpp +; 1 using INTPTR = const int *; +; 2 int foo(INTPTR ParamPtr, unsigned ParamUnsigned, bool ParamBool) { +; 3 if (ParamBool) { +; 4 typedef int INTEGER; +; 5 const INTEGER CONSTANT = 7; +; 6 return CONSTANT; +; 7 } +; 8 return ParamUnsigned; +; 9 } + +; Print low level details. +; The following command prints low level information that includes +; offsets within the debug information section, debug location +; operands, linkage names, etc. + +; RUN: llvm-debuginfo-analyzer --attribute=all \ +; RUN: --print=all \ +; RUN: %p/Inputs/test-codeview-clang.o 2>&1 | \ +; RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +; ONE: Logical View: +; ONE-NEXT: [0x0000000000][000] {File} '{{.*}}test-codeview-clang.o' -> COFF-x86-64 +; ONE-EMPTY: +; ONE-NEXT: [0x0000000000][001] {CompileUnit} 'test.cpp' +; ONE-NEXT: [0x0000000000][002] {Producer} 'clang version 15.0.0 {{.*}}' +; ONE-NEXT: {Directory} 'test.cpp' +; ONE-NEXT: {Directory} 'x:/tests/input' +; ONE-NEXT: {File} 'general' +; ONE-NEXT: {File} 'test.cpp' +; ONE-NEXT: {Public} 'foo' [0x0000000000:0x0000000046] +; ONE-NEXT: [0x0000000000][002] {TypeAlias} 'INTPTR' -> [0x0000001001]'* const int' +; ONE-NEXT: [0x0000000030][002] {BaseType} 'bool' +; ONE-NEXT: [0x0000000000][002] {Function} extern not_inlined 'foo' -> [0x0000000074]'int' +; ONE-NEXT: [0x0000000000][003] {Range} Lines 2:9 [0x0000000000:0x0000000046] +; ONE-NEXT: [0x0000000000][003] {Linkage} 0x1 '?foo@@YAHPEBHI_N@Z' +; ONE-NEXT: [0x0000000000][003] {TypeAlias} 'INTEGER' -> [0x0000000074]'int' +; ONE-NEXT: [0x0000000000][003] {Parameter} 'ParamBool' -> [0x0000000030]'bool' +; ONE-NEXT: [0x0000000000][004] {Coverage} 70.00% (49/70) +; ONE-NEXT: [0x0000000000][004] {Location} Lines 3:9 [0x0000000016:0x0000000047] +; ONE-NEXT: [0x0000000000][005] {Entry} frame_pointer_rel 27 +; ONE-NEXT: [0x0000000000][003] {Parameter} 'ParamPtr' -> [0x0000001001]'* const int' +; ONE-NEXT: [0x0000000000][004] {Coverage} 70.00% (49/70) +; ONE-NEXT: [0x0000000000][004] {Location} Lines 3:9 [0x0000000016:0x0000000047] +; ONE-NEXT: [0x0000000000][005] {Entry} frame_pointer_rel 8 +; ONE-NEXT: [0x0000000000][003] {Parameter} 'ParamUnsigned' -> [0x0000000075]'unsigned' +; ONE-NEXT: [0x0000000000][004] {Coverage} 70.00% (49/70) +; ONE-NEXT: [0x0000000000][004] {Location} Lines 3:9 [0x0000000016:0x0000000047] +; ONE-NEXT: [0x0000000000][005] {Entry} frame_pointer_rel 20 +; ONE-NEXT: [0x0000000000][003] {Block} +; ONE-NEXT: [0x0000000000][004] {Range} Lines ?:? [0x0000000021:0x0000000035] +; ONE-NEXT: [0x0000000000][004] {Variable} 'CONSTANT' -> [0x0000001000]'const int' +; ONE-NEXT: [0x0000000000][005] {Coverage} 105.00% (21/20) +; ONE-NEXT: [0x0000000000][005] {Location} Lines ?:? [0x0000000021:0x0000000036] +; ONE-NEXT: [0x0000000000][006] {Entry} frame_pointer_rel 4 +; ONE-EMPTY: +; ONE-NEXT: [0x0000000021][004] {Source} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000021][004] 5 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000021][004] {Code} 'movl $0x7, 0x4(%rsp)' +; ONE-NEXT: [0x0000000029][004] 6 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000029][004] {Code} 'movl $0x7, 0x1c(%rsp)' +; ONE-NEXT: [0x0000000031][004] {Code} 'jmp 0x8' +; ONE-NEXT: [0x0000000000][003] 2 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000000][003] {Code} 'subq $0x20, %rsp' +; ONE-NEXT: [0x0000000004][003] {Code} 'andb $0x1, %r8b' +; ONE-NEXT: [0x0000000008][003] {Code} 'movb %r8b, 0x1b(%rsp)' +; ONE-NEXT: [0x000000000d][003] {Code} 'movl %edx, 0x14(%rsp)' +; ONE-NEXT: [0x0000000011][003] {Code} 'movq %rcx, 0x8(%rsp)' +; ONE-NEXT: [0x0000000016][003] 3 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000016][003] {Code} 'testb $0x1, 0x1b(%rsp)' +; ONE-NEXT: [0x000000001b][003] {Code} 'je 0x15' +; ONE-NEXT: [0x0000000036][003] 8 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x0000000036][003] {Code} 'movl 0x14(%rsp), %eax' +; ONE-NEXT: [0x000000003a][003] {Code} 'movl %eax, 0x1c(%rsp)' +; ONE-NEXT: [0x000000003e][003] 9 {Line} 'x:/tests/input/general/test.cpp' +; ONE-NEXT: [0x000000003e][003] {Code} 'movl 0x1c(%rsp), %eax' +; ONE-NEXT: [0x0000000042][003] {Code} 'addq $0x20, %rsp' +; ONE-NEXT: [0x0000000046][003] {Code} 'retq' +; ONE-NEXT: [0x0000000074][002] {BaseType} 'int' +; ONE-NEXT: [0x0000000075][002] {BaseType} 'unsigned' +; ONE-EMPTY: +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Element Total Printed +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Scopes 3 3 +; ONE-NEXT: Symbols 4 4 +; ONE-NEXT: Types 5 5 +; ONE-NEXT: Lines 21 21 +; ONE-NEXT: ----------------------------- +; ONE-NEXT: Total 33 33 +; ONE-EMPTY: +; ONE-NEXT: Scope Sizes: +; ONE-EMPTY: +; ONE-NEXT: Totals by lexical level: +; ONE-EMPTY: Index: llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt =================================================================== --- llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt +++ llvm/unittests/DebugInfo/LogicalView/CMakeLists.txt @@ -7,6 +7,7 @@ ) add_llvm_unittest_with_input_files(DebugInfoLogicalViewTests + CodeViewReaderTest.cpp CommandLineOptionsTest.cpp CompareElementsTest.cpp ELFReaderTest.cpp Index: llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/LogicalView/CodeViewReaderTest.cpp @@ -0,0 +1,475 @@ +//===- llvm/unittest/DebugInfo/LogicalView/CodeViewReaderTest.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/LVCompare.h" +#include "llvm/DebugInfo/LogicalView/Core/LVLine.h" +#include "llvm/DebugInfo/LogicalView/Core/LVScope.h" +#include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" +#include "llvm/DebugInfo/LogicalView/Core/LVType.h" +#include "llvm/DebugInfo/LogicalView/LVReaderHandler.h" +#include "llvm/Support/COM.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::logicalview; + +extern const char *TestMainArgv0; + +namespace { + +const char *CodeViewClang = "test-codeview-clang.o"; +const char *CodeViewMsvc = "test-codeview-msvc.o"; +const char *CodeViewPdbMsvc = "test-codeview-pdb-msvc.o"; + +// Helper function to get the first scope child from the given parent. +LVScope *getFirstScopeChild(LVScope *Parent) { + EXPECT_NE(Parent, nullptr); + const LVScopes *Scopes = Parent->getScopes(); + EXPECT_NE(Scopes, nullptr); + EXPECT_EQ(Scopes->size(), 1); + + LVScopes::const_iterator Iter = Scopes->begin(); + LVScope *Child = *Iter; + EXPECT_NE(Child, nullptr); + return Child; +} + +// Helper function to create a reader. +LVReader *createReader(LVReaderHandler &ReaderHandler, + SmallString<128> &InputsDir, StringRef Filename) { + SmallString<128> ObjectName(InputsDir); + llvm::sys::path::append(ObjectName, Filename); + + Expected ReaderOrErr = + ReaderHandler.createReader(std::string(ObjectName)); + EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded()); + LVReader *Reader = *ReaderOrErr; + EXPECT_NE(Reader, nullptr); + return Reader; +} + +// Check the logical elements basic properties (Clang - Codeview). +void checkElementPropertiesClangCodeview(LVReader *Reader) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = + static_cast(getFirstScopeChild(Root)); + LVScopeFunction *Function = + static_cast(getFirstScopeChild(CompileUnit)); + + EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); + EXPECT_EQ(Root->getName(), CodeViewClang); + + EXPECT_EQ(CompileUnit->getBaseAddress(), 0); + EXPECT_TRUE(CompileUnit->getProducer().startswith("clang")); + EXPECT_EQ(CompileUnit->getName(), "test.cpp"); + + EXPECT_EQ(Function->lineCount(), 16); + EXPECT_EQ(Function->scopeCount(), 1); + EXPECT_EQ(Function->symbolCount(), 3); + EXPECT_EQ(Function->typeCount(), 1); + EXPECT_EQ(Function->rangeCount(), 1); + + const LVLocations *Ranges = Function->getRanges(); + EXPECT_NE(Ranges, nullptr); + ASSERT_EQ(Ranges->size(), 1); + LVLocations::const_iterator IterLocation = Ranges->begin(); + LVLocation *Location = (*IterLocation); + EXPECT_STREQ(Location->getIntervalInfo().c_str(), + "{Range} Lines 2:9 [0x0000000000:0x0000000046]"); + + LVRange RangeList; + Function->getRanges(RangeList); + + const LVRangeEntries &RangeEntries = RangeList.getEntries(); + ASSERT_EQ(RangeEntries.size(), 2); + LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin(); + LVRangeEntry RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0); + EXPECT_EQ(RangeEntry.upper(), 0x46); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + ++IterRanges; + RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0x21); + EXPECT_EQ(RangeEntry.upper(), 0x35); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); + ASSERT_EQ(PublicNames.size(), 1); + LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); + LVScope *Foo = (*IterNames).first; + EXPECT_EQ(Foo->getName(), "foo"); + EXPECT_EQ(Foo->getLineNumber(), 0); + LVNameInfo NameInfo = (*IterNames).second; + EXPECT_EQ(NameInfo.first, 0); + EXPECT_EQ(NameInfo.second, 0x46); + + // Lines (debug and assembler) for 'foo'. + const LVLines *Lines = Foo->getLines(); + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 0x10); +} + +// Check the logical elements basic properties (MSVC - Codeview). +void checkElementPropertiesMsvcCodeview(LVReader *Reader) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = + static_cast(getFirstScopeChild(Root)); + LVScopeFunction *Function = + static_cast(getFirstScopeChild(CompileUnit)); + + EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); + EXPECT_EQ(Root->getName(), CodeViewMsvc); + + EXPECT_EQ(CompileUnit->getBaseAddress(), 0); + EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft")); + EXPECT_EQ(CompileUnit->getName(), "test.cpp"); + + EXPECT_EQ(Function->lineCount(), 14); + EXPECT_EQ(Function->scopeCount(), 1); + EXPECT_EQ(Function->symbolCount(), 3); + EXPECT_EQ(Function->typeCount(), 0); + EXPECT_EQ(Function->rangeCount(), 1); + + const LVLocations *Ranges = Function->getRanges(); + EXPECT_NE(Ranges, nullptr); + ASSERT_EQ(Ranges->size(), 1); + LVLocations::const_iterator IterLocation = Ranges->begin(); + LVLocation *Location = (*IterLocation); + EXPECT_STREQ(Location->getIntervalInfo().c_str(), + "{Range} Lines 2:9 [0x0000000000:0x0000000031]"); + + LVRange RangeList; + Function->getRanges(RangeList); + + const LVRangeEntries &RangeEntries = RangeList.getEntries(); + ASSERT_EQ(RangeEntries.size(), 2); + LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin(); + LVRangeEntry RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0); + EXPECT_EQ(RangeEntry.upper(), 0x31); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + ++IterRanges; + RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0x1b); + EXPECT_EQ(RangeEntry.upper(), 0x28); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); + ASSERT_EQ(PublicNames.size(), 1); + LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); + LVScope *Foo = (*IterNames).first; + EXPECT_EQ(Foo->getName(), "foo"); + EXPECT_EQ(Foo->getLineNumber(), 0); + LVNameInfo NameInfo = (*IterNames).second; + EXPECT_EQ(NameInfo.first, 0); + EXPECT_EQ(NameInfo.second, 0x31); + + // Lines (debug and assembler) for 'foo'. + const LVLines *Lines = Foo->getLines(); + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 0x0e); +} + +// Check the logical elements basic properties (MSVC - PDB). +void checkElementPropertiesMsvcCodeviewPdb(LVReader *Reader) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = + static_cast(getFirstScopeChild(Root)); + LVScopeFunction *Function = + static_cast(getFirstScopeChild(CompileUnit)); + + EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64"); + EXPECT_EQ(Root->getName(), CodeViewPdbMsvc); + + EXPECT_EQ(CompileUnit->getBaseAddress(), 0); + EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft")); + EXPECT_EQ(CompileUnit->getName(), "test.cpp"); + + EXPECT_EQ(Function->lineCount(), 14); + EXPECT_EQ(Function->scopeCount(), 1); + EXPECT_EQ(Function->symbolCount(), 3); + EXPECT_EQ(Function->typeCount(), 0); + EXPECT_EQ(Function->rangeCount(), 1); + + const LVLocations *Ranges = Function->getRanges(); + EXPECT_NE(Ranges, nullptr); + ASSERT_EQ(Ranges->size(), 1); + LVLocations::const_iterator IterLocation = Ranges->begin(); + LVLocation *Location = (*IterLocation); + EXPECT_STREQ(Location->getIntervalInfo().c_str(), + "{Range} Lines 2:9 [0x0000000000:0x0000000031]"); + + LVRange RangeList; + Function->getRanges(RangeList); + + const LVRangeEntries &RangeEntries = RangeList.getEntries(); + ASSERT_EQ(RangeEntries.size(), 2); + LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin(); + LVRangeEntry RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0); + EXPECT_EQ(RangeEntry.upper(), 0x31); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + ++IterRanges; + RangeEntry = *IterRanges; + EXPECT_EQ(RangeEntry.lower(), 0x1b); + EXPECT_EQ(RangeEntry.upper(), 0x28); + EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0); + EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?"); + EXPECT_EQ(RangeEntry.scope()->getOffset(), 0); + + const LVPublicNames &PublicNames = CompileUnit->getPublicNames(); + ASSERT_EQ(PublicNames.size(), 1); + LVPublicNames::const_iterator IterNames = PublicNames.cbegin(); + LVScope *Foo = (*IterNames).first; + EXPECT_EQ(Foo->getName(), "foo"); + EXPECT_EQ(Foo->getLineNumber(), 0); + LVNameInfo NameInfo = (*IterNames).second; + EXPECT_EQ(NameInfo.first, 0); + EXPECT_EQ(NameInfo.second, 0x31); + + // Lines (debug and assembler) for 'foo'. + const LVLines *Lines = Foo->getLines(); + EXPECT_NE(Lines, nullptr); + ASSERT_EQ(Lines->size(), 0x0e); +} + +struct SelectionInfo { + const char *Name; + LVElementGetFunction Function; +}; + +// Check the logical elements selection. +void checkElementSelection(LVReader *Reader, std::vector &Data, + size_t Size) { + LVScopeRoot *Root = Reader->getScopesRoot(); + LVScopeCompileUnit *CompileUnit = + static_cast(getFirstScopeChild(Root)); + + // Get the matched elements. + LVElements MatchedElements = CompileUnit->getMatchedElements(); + std::map MapElements; + for (LVElement *Element : MatchedElements) + MapElements[Element->getName()] = Element; + ASSERT_EQ(MapElements.size(), Size); + + std::map::iterator Iter = MapElements.begin(); + for (const SelectionInfo &Entry : Data) { + // Get matched element. + EXPECT_NE(Iter, MapElements.end()); + LVElement *Element = Iter->second; + EXPECT_NE(Element, nullptr); + EXPECT_NE(Element->getName().find(Entry.Name), StringRef::npos); + EXPECT_EQ((Element->*Entry.Function)(), 1); + ++Iter; + } + + // Get the parents for the matched elements. + LVScopes MatchedScopes = CompileUnit->getMatchedScopes(); + std::set SetScopes; + for (LVScope *Scope : MatchedScopes) + SetScopes.insert(Scope->getName()); + ASSERT_EQ(SetScopes.size(), 3); + + // Parents of selected elements. + std::set::iterator IterScope; + IterScope = SetScopes.find("foo"); + EXPECT_NE(IterScope, SetScopes.end()); + IterScope = SetScopes.find("foo::?"); + EXPECT_NE(IterScope, SetScopes.end()); + IterScope = SetScopes.find("test.cpp"); + EXPECT_NE(IterScope, SetScopes.end()); +} + +// Check the logical elements comparison. +void checkElementComparison(LVReader *Reference, LVReader *Target) { + LVCompare Compare(nulls()); + Error Err = Compare.execute(Reference, Target); + ASSERT_THAT_ERROR(std::move(Err), Succeeded()); + + // Get comparison table. + LVPassTable PassTable = Compare.getPassTable(); + ASSERT_EQ(PassTable.size(), 2); + + LVReader *Reader; + LVElement *Element; + LVComparePass Pass; + + // Reference: Missing TypeDefinition 'INTEGER' + std::tie(Reader, Element, Pass) = PassTable[0]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Reference); + EXPECT_EQ(Element->getLevel(), 3); + EXPECT_EQ(Element->getLineNumber(), 0); + EXPECT_EQ(Element->getName(), "INTEGER"); + EXPECT_EQ(Pass, LVComparePass::Missing); + + // Target: Added TypeDefinition 'INTEGER' + std::tie(Reader, Element, Pass) = PassTable[1]; + EXPECT_NE(Reader, nullptr); + EXPECT_NE(Element, nullptr); + EXPECT_EQ(Reader, Target); + EXPECT_EQ(Element->getLevel(), 4); + EXPECT_EQ(Element->getLineNumber(), 0); + EXPECT_EQ(Element->getName(), "INTEGER"); + EXPECT_EQ(Pass, LVComparePass::Added); +} + +// Logical elements properties. +void elementProperties(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setAttributeFormat(); + ReaderOptions.setAttributeFilename(); + ReaderOptions.setAttributeProducer(); + ReaderOptions.setAttributePublics(); + ReaderOptions.setAttributeRange(); + ReaderOptions.setAttributeLocation(); + ReaderOptions.setPrintAll(); + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical elements properties. + LVReader *Reader = createReader(ReaderHandler, InputsDir, CodeViewClang); + checkElementPropertiesClangCodeview(Reader); + ReaderHandler.deleteReader(Reader); + + Reader = createReader(ReaderHandler, InputsDir, CodeViewMsvc); + checkElementPropertiesMsvcCodeview(Reader); + ReaderHandler.deleteReader(Reader); + + Reader = createReader(ReaderHandler, InputsDir, CodeViewPdbMsvc); + checkElementPropertiesMsvcCodeviewPdb(Reader); + ReaderHandler.deleteReader(Reader); +} + +// Logical elements selection. +void elementSelection(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintAll(); + + ReaderOptions.setSelectIgnoreCase(); + ReaderOptions.setSelectUseRegex(); + + ReaderOptions.setReportList(); // Matched elements. + ReaderOptions.setReportView(); // Parents for matched elements. + + // Add patterns. + ReaderOptions.Select.Generic.insert("foo"); + ReaderOptions.Select.Generic.insert("movl[ \t]?%"); + ReaderOptions.Select.Generic.insert("INT[a-z]*"); + ReaderOptions.Select.Generic.insert("CONSTANT"); + + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical elements selection. + std::vector DataClang = { + {"* const int", &LVElement::getIsType}, + {"CONSTANT", &LVElement::getIsSymbol}, + {"INTEGER", &LVElement::getIsType}, + {"INTPTR", &LVElement::getIsType}, + {"ParamPtr", &LVElement::getIsSymbol}, + {"const int", &LVElement::getIsType}, + {"foo", &LVElement::getIsScope}, + {"foo::?", &LVElement::getIsScope}, + {"int", &LVElement::getIsType}, + {"movl", &LVElement::getIsLine}, + {"movl", &LVElement::getIsLine}}; + LVReader *Reader = createReader(ReaderHandler, InputsDir, CodeViewClang); + checkElementSelection(Reader, DataClang, DataClang.size()); + ReaderHandler.deleteReader(Reader); + + std::vector DataMsvc = {{"* const int", &LVElement::getIsType}, + {"CONSTANT", &LVElement::getIsSymbol}, + {"INTEGER", &LVElement::getIsType}, + {"INTPTR", &LVElement::getIsType}, + {"ParamPtr", &LVElement::getIsSymbol}, + {"const int", &LVElement::getIsType}, + {"foo", &LVElement::getIsScope}, + {"foo::?", &LVElement::getIsScope}, + {"int", &LVElement::getIsType}, + {"movl", &LVElement::getIsLine}}; + Reader = createReader(ReaderHandler, InputsDir, CodeViewMsvc); + checkElementSelection(Reader, DataMsvc, DataMsvc.size()); + ReaderHandler.deleteReader(Reader); +} + +// Compare logical elements. +void compareElements(SmallString<128> &InputsDir) { + // Reader options. + LVOptions ReaderOptions; + ReaderOptions.setAttributeOffset(); + ReaderOptions.setPrintLines(); + ReaderOptions.setPrintSymbols(); + ReaderOptions.setPrintTypes(); + ReaderOptions.setCompareLines(); + ReaderOptions.setCompareSymbols(); + ReaderOptions.setCompareTypes(); + + ReaderOptions.resolveDependencies(); + + std::vector Objects; + ScopedPrinter W(outs()); + LVReaderHandler ReaderHandler(Objects, W, ReaderOptions); + + // Check logical comparison. + LVReader *Reference = createReader(ReaderHandler, InputsDir, CodeViewClang); + LVReader *Target = createReader(ReaderHandler, InputsDir, CodeViewMsvc); + checkElementComparison(Reference, Target); + ReaderHandler.deleteReader(Reference); + ReaderHandler.deleteReader(Target); +} + +TEST(LogicalViewTest, CodeViewReader) { + // Initialize targets and assembly printers/parsers. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + InitializeAllDisassemblers(); + + llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded); + + SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0); + + // Logical elements general properties and selection. + elementProperties(InputsDir); + elementSelection(InputsDir); + + // Compare logical elements. + compareElements(InputsDir); +} + +} // namespace