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;
virtual void setDiscriminator(uint32_t Value) {}
// Process the values for a DW_TAG_enumerator.
- virtual std::string getValue() const { return {}; }
+ virtual StringRef getValue() const { return {}; }
virtual void setValue(StringRef Value) {}
virtual size_t getValueIndex() const { return 0; }
StringRef
accessibilityString(uint32_t Access = dwarf::DW_ACCESS_private) const;
+ // CodeView Accessibility Codes.
+ std::optional<uint32_t> getAccessibilityCode(codeview::MemberAccess Access);
+ void setAccessibilityCode(codeview::MemberAccess Access) {
+ if (std::optional<uint32_t> Code = getAccessibilityCode(Access))
+ AccessibilityCode = Code.value();
+ }
+
// DWARF Inline Codes.
uint32_t getInlineCode() const { return InlineCode; }
void setInlineCode(uint32_t Code) { InlineCode = Code; }
StringRef
virtualityString(uint32_t Virtuality = dwarf::DW_VIRTUALITY_none) const;
+ // CodeView Virtuality Codes.
+ std::optional<uint32_t> getVirtualityCode(codeview::MethodKind Virtuality);
+ void setVirtualityCode(codeview::MethodKind Virtuality) {
+ if (std::optional<uint32_t> Code = getVirtualityCode(Virtuality))
+ VirtualityCode = Code.value();
+ }
+
// DWARF Extern Codes.
StringRef externalString() const;
assert(Scope && Scope->isCompileUnit() && "Scope is not a compile unit");
CompileUnit = static_cast<LVScopeCompileUnit *>(Scope);
}
+ void setCompileUnitCPUType(codeview::CPUType Type) {
+ CompileUnit->setCPUType(Type);
+ }
+ codeview::CPUType getCompileUnitCPUType() {
+ return CompileUnit->getCPUType();
+ }
// Access to the scopes root.
LVScopeRoot *getScopesRoot() const { return Root; }
// 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;
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.
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();
// Returns the unique string pool instance.
LVStringPool &getStringPool();
+using LVStringRefs = std::vector<StringRef>;
+using LVLexicalComponent = std::tuple<StringRef, StringRef>;
+using LVLexicalIndex =
+ std::tuple<LVStringRefs::size_type, LVStringRefs::size_type>;
+
// Used to record specific characteristics about the objects.
template <typename T> class LVProperties {
SmallBitVector Bits = SmallBitVector(static_cast<unsigned>(T::LastEntry) + 1);
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 = {});
+
// These are the values assigned to the debug location record IDs.
// See DebugInfo/CodeView/CodeViewSymbols.def.
// S_DEFRANGE 0x113f
void setBitSize(uint32_t Size) override { BitSize = Size; }
// Process the values for a DW_AT_const_value.
- std::string getValue() const override {
- return std::string(getStringPool().getString(ValueIndex));
+ StringRef getValue() const override {
+ return getStringPool().getString(ValueIndex);
}
void setValue(StringRef Value) override {
ValueIndex = getStringPool().getIndex(Value);
~LVTypeEnumerator() = default;
// Process the values for a DW_TAG_enumerator.
- std::string getValue() const override {
- return std::string(getStringPool().getString(ValueIndex));
+ StringRef getValue() const override {
+ return getStringPool().getString(ValueIndex);
}
void setValue(StringRef Value) override {
ValueIndex = getStringPool().getIndex(Value);
~LVTypeParam() = default;
// Template parameter value.
- std::string getValue() const override {
- return std::string(getStringPool().getString(ValueIndex));
+ StringRef getValue() const override {
+ return getStringPool().getString(ValueIndex);
}
void setValue(StringRef Value) override {
ValueIndex = getStringPool().getIndex(Value);
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 = {});
#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 {
// 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<LVScope *, std::unique_ptr<LVLines>>;
+ LVInlineeLine CUInlineeLines;
+
// Instruction lines for a logical scope. These instructions are fetched
// during its merge with the debug lines.
LVDoubleMap<LVSectionIndex, LVScope *, LVLines *> ScopeInstructions;
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,
LVBinaryReader &operator=(const LVBinaryReader &) = delete;
virtual ~LVBinaryReader() = default;
+ void addInlineeLines(LVScope *Scope, LVLines &Lines) {
+ CUInlineeLines.emplace(Scope, std::make_unique<LVLines>(std::move(Lines)));
+ }
+
+ // Convert Segment::Offset pair to absolute address.
+ LVAddress linearAddress(uint16_t Segment, uint32_t Offset,
+ LVAddress Addendum = 0) {
+ return ImageBaseAddress + (Segment * VirtualAddress) + Offset + Addendum;
+ }
+
void addToSymbolTable(StringRef Name, LVScope *Function,
LVSectionIndex SectionIndex = 0);
void addToSymbolTable(StringRef Name, LVAddress Address,
--- /dev/null
+//===-- 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<codeview::CVType> {
+ static size_t length(const codeview::CVType &Item) { return Item.length(); }
+ static ArrayRef<uint8_t> bytes(const codeview::CVType &Item) {
+ return Item.data();
+ }
+};
+
+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<StringRef, 16>;
+
+// The ELF reader uses 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<LVScope *>;
+ 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<llvm::pdb::InputFile> TypeServer;
+ std::shared_ptr<LazyRandomTypeCollection> PrecompHeader;
+
+ // Persistance data when loading a type server.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = nullptr;
+ std::unique_ptr<MemoryBuffer> MemBuffer;
+ std::unique_ptr<llvm::pdb::IPDBSession> Session;
+ std::unique_ptr<llvm::pdb::NativeSession> PdbSession;
+
+ // Persistance data when loading a precompiled header.
+ BumpPtrAllocator BuilderAllocator;
+ std::unique_ptr<AppendingTypeTableBuilder> Builder;
+ std::unique_ptr<BinaryItemStream<CVType>> ItemStream;
+ std::unique_ptr<BinaryStreamReader> ReaderPrecomp;
+ std::vector<CVType> TypeArray;
+ CVTypeArray TypeStream;
+ CVTypeArray CVTypesPrecomp;
+
+ // Persistance data when loading an executable file.
+ std::unique_ptr<MemoryBuffer> BinaryBuffer;
+ std::unique_ptr<llvm::object::Binary> 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<const llvm::object::coff_section *,
+ std::vector<llvm::object::RelocationRef>>;
+ 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<StringRef>
+ 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).
+ /// TODO: It can be moved to the COFF library.
+ Error initializeFileAndStringTables(BinaryStreamReader &Reader);
+
+ Error createLines(const FixedStreamArray<LineNumberEntry> &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 StringRef 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;
+
+ 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
--- /dev/null
+//===-- 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/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 <stack>
+#include <utility>
+
+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 {
+ LVCodeViewReader *Reader;
+ const llvm::object::coff_section *CoffSection;
+ StringRef SectionContents;
+
+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<uint8_t> 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;
+};
+
+class LVElement;
+class LVScope;
+class LVSymbol;
+class LVType;
+
+// Visitor for CodeView symbol streams found in COFF object files and PDB files.
+class LVSymbolVisitor final : public SymbolVisitorCallbacks {
+ LVCodeViewReader *Reader;
+ ScopedPrinter &W;
+ LVLogicalVisitor *LogicalVisitor;
+ LazyRandomTypeCollection &Types;
+ LazyRandomTypeCollection &Ids;
+ LVSymbolVisitorDelegate *ObjDelegate;
+ LVShared *Shared;
+
+ // 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;
+ bool InFunctionScope = false;
+ bool IsCompileUnit = false;
+
+ // Register for the locals and parameters symbols in the current frame.
+ RegisterId LocalFrameRegister = RegisterId::NONE;
+ RegisterId ParamFrameRegister = RegisterId::NONE;
+
+ void printLocalVariableAddrRange(const LocalVariableAddrRange &Range,
+ uint32_t RelocationOffset);
+ void printLocalVariableAddrGap(ArrayRef<LocalVariableAddrGap> 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;
+ 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<llvm::pdb::InputFile> TypeServer = nullptr;
+ std::shared_ptr<LazyRandomTypeCollection> PrecompHeader = nullptr;
+
+ std::shared_ptr<LVShared> Shared;
+
+ // 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<LVScope *>;
+ 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<uint32_t, StringRef>;
+ using LVInlineeInfo = std::map<TypeIndex, LVInlineeEntry>;
+ LVInlineeInfo InlineeInfo;
+
+ Error visitFieldListMemberStream(TypeIndex TI, LVElement *Element,
+ ArrayRef<uint8_t> 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);
+
+ // Current elements during the processing of a RecordType or RecordSymbol.
+ // They are shared with the SymbolVisitor.
+ LVElement *CurrentElement = nullptr;
+ LVScope *CurrentScope = nullptr;
+ LVSymbol *CurrentSymbol = nullptr;
+ LVType *CurrentType = nullptr;
+
+ // Input source in the case of type server or precompiled header.
+ void setInput(std::shared_ptr<llvm::pdb::InputFile> TypeServer) {
+ this->TypeServer = TypeServer;
+ }
+ void setInput(std::shared_ptr<LazyRandomTypeCollection> 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);
+ }
+
+ LVElement *getElement(uint32_t StreamIdx, TypeIndex TI,
+ LVScope *Parent = nullptr);
+ LVShared *getShared() { return Shared.get(); }
+
+ 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;
+
+ 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 <typename T>
+ Error visitKnownMember(CVMemberRecord &Record,
+ TypeVisitorCallbacks &Callbacks, TypeIndex TI,
+ LVElement *Element) {
+ TypeRecordKind RK = static_cast<TypeRecordKind>(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 <typename T>
+ Error visitKnownRecord(CVType &Record, TypeIndex TI, LVElement *Element) {
+ TypeRecordKind RK = static_cast<TypeRecordKind>(Record.kind());
+ T KnownRecord(RK);
+ if (Error Err = TypeDeserializer::deserializeAs(
+ const_cast<CVType &>(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
add_lv_impl_folder(Readers
LVReaderHandler.cpp
Readers/LVBinaryReader.cpp
+ Readers/LVCodeViewReader.cpp
+ Readers/LVCodeViewVisitor.cpp
Readers/LVELFReader.cpp
)
LINK_COMPONENTS
BinaryFormat
+ Demangle
Object
MC
Support
TargetParser
DebugInfoDWARF
+ DebugInfoCodeView
+ DebugInfoPDB
)
#include "llvm/DebugInfo/LogicalView/Core/LVType.h"
using namespace llvm;
+using namespace llvm::codeview;
using namespace llvm::logicalview;
#define DEBUG_TYPE "Element"
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()) {
}
}
+std::optional<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 std::nullopt;
+ }
+}
+
StringRef LVElement::externalString() const {
return getIsExternal() ? "extern" : StringRef();
}
}
}
+std::optional<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 std::nullopt;
+ }
+}
+
void LVElement::resolve() {
if (getIsResolved())
return;
// Get the filename for given object.
StringRef LVReader::getFilename(LVObject *Object, size_t Index) const {
+ // TODO: The current CodeView Reader implementation does not have support
+ // for multiple compile units. Until we have 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 =
// 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()) {
// DW_AT_external DW_FORM_flag_present
// 00000070 DW_TAG_subprogram "bar"
// DW_AT_specification DW_FORM_ref4 0x00000048
+ // 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()) {
}
}
+void LVScopeRoot::transformScopedName() {
+ // Recursively transform all names.
+ std::function<void(LVScope * Parent)> 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);
}
};
return Name;
}
+
+using LexicalEntry = std::pair<size_t, size_t>;
+using LexicalIndexes = SmallVector<LexicalEntry, 10>;
+
+static LexicalIndexes getAllLexicalIndexes(StringRef Name) {
+ if (Name.empty())
+ return {};
+
+ size_t AngleCount = 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;
+ break;
+ case '>':
+ --AngleCount;
+ break;
+ case ':':
+ ++ColonSeen;
+ break;
+ }
+ if (ColonSeen == 2) {
+ if (!AngleCount) {
+ Indexes.push_back(LexicalEntry(Current, Index - 2));
+ Current = Index + 1;
+ LLVM_DEBUG({ PrintLexicalEntry(); });
+ }
+ ColonSeen = 0;
+ continue;
+ }
+ }
+
+ // 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;
+}
}
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
+ // typedefs 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());
//===----------------------------------------------------------------------===//
#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;
auto CreateOneReader = [&]() -> std::unique_ptr<LVReader> {
if (Input.is<ObjectFile *>()) {
ObjectFile &Obj = *Input.get<ObjectFile *>();
+ if (Obj.isCOFF()) {
+ COFFObjectFile *COFF = cast<COFFObjectFile>(&Obj);
+ return std::make_unique<LVCodeViewReader>(Filename, FileFormatName,
+ *COFF, W, ExePath);
+ }
if (Obj.isELF() || Obj.isMachO())
return std::make_unique<LVELFReader>(Filename, FileFormatName, Obj, W);
}
+ if (Input.is<PDBFile *>()) {
+ PDBFile &Pdb = *Input.get<PDBFile *>();
+ return std::make_unique<LVCodeViewReader>(Filename, FileFormatName, Pdb,
+ W, ExePath);
+ }
return nullptr;
};
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<IPDBSession> Session;
+ if (Error Err = loadDataForEXE(PDB_ReaderType::Native, ExePath, Session)) {
+ consumeError(std::move(Err));
+ return {};
+ }
+ // We have a candidate for the executable image.
+ Expected<std::string> 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(ObjPath)) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> 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 derived 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<StringRef> 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<StringRef> 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<std::string> PdbPath = NativeSession::searchForPdb({Filename});
+ if (errorToErrorCode(PdbPath.takeError())) {
+ return createStringError(
+ errc::not_supported,
+ "Binary object format in '%s' does not have debug info.",
+ Filename.str().c_str());
+ }
+ // Process the matching PDB file and pass the executable filename.
+ return handleFile(Readers, PdbPath.get(), Filename);
+ }
+
Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buffer);
if (errorToErrorCode(BinOrErr.takeError())) {
return createStringError(errc::not_supported,
Filename.str().c_str());
}
+Error LVReaderHandler::handleObject(LVReaders &Readers, StringRef Filename,
+ StringRef Buffer, StringRef ExePath) {
+ std::unique_ptr<IPDBSession> Session;
+ if (Error Err = loadDataForPDB(PDB_ReaderType::Native, Filename, Session))
+ return createStringError(errorToErrorCode(std::move(Err)), "%s",
+ Filename.str().c_str());
+
+ std::unique_ptr<NativeSession> PdbSession;
+ PdbSession.reset(static_cast<NativeSession *>(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) {
});
}
+void LVBinaryReader::mapVirtualAddress(const object::COFFObjectFile &COFFObj) {
+ ErrorOr<uint64_t> 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<StringRef> 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;
}
}
+// Traverse the scopes for the given 'Function' looking for any inlined
+// scopes with inlined lines, which are found in 'CUInlineeLines'.
+void LVBinaryReader::includeInlineeLines(LVSectionIndex SectionIndex,
+ LVScope *Function) {
+ SmallVector<LVInlineeLine::iterator> InlineeIters;
+ std::function<void(LVScope * Parent)> 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);
+
+ // TODO: Convert this into a reference.
+ LVLines *InlineeLines = InlineeIter->second.get();
+ 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();
+ }
+ 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");
--- /dev/null
+//===-- 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"
+
+StringRef 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";
+ }
+ llvm_unreachable("Unknown SymbolKind::Kind");
+}
+
+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))
+ Symbol = "";
+}
+
+Expected<StringRef>
+LVCodeViewReader::getFileNameForFileOffset(uint32_t FileOffset,
+ const SymbolGroup *SG) {
+ if (SG) {
+ Expected<StringRef> 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<FileChecksumEntry>::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<StringRef> 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<StringRef> 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<StringError>("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<StringRef> 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<StringRef> 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<LineNumberEntry> &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 = createLineDebug();
+ 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<StringRef> 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 same directory as the
+ // input file.
+ ServerName = createAlternativePath(ServerName);
+ BuffOrErr = MemoryBuffer::getFile(ServerName);
+ if (BuffOrErr.getError()) {
+ // For the error message, use the original type server name.
+ return createStringError(errc::bad_file_descriptor,
+ "File '%s' does not exist.",
+ TS.getName().str().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))
+ return createStringError(errorToErrorCode(std::move(Err)), "%s",
+ ServerName.c_str());
+
+ PdbSession.reset(static_cast<NativeSession *>(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<InfoStream &> 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 will require the input associated with the loaded object file.
+ TypeServer = std::make_shared<InputFile>(&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(ServerName);
+ if (BuffOrErr.getError()) {
+ // For the error message, use the original type server name.
+ return createStringError(errc::bad_file_descriptor,
+ "File '%s' does not exist.",
+ Precomp.getPrecompFilePath().str().c_str());
+ }
+ }
+ MemBuffer = std::move(BuffOrErr.get());
+
+ Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(*MemBuffer);
+ if (errorToErrorCode(BinOrErr.takeError()))
+ return createStringError(errc::not_supported,
+ "Binary object format in '%s' is not supported.",
+ ServerName.c_str());
+
+ Binary &BinaryObj = *BinOrErr.get();
+ if (!BinaryObj.isCOFF())
+ return createStringError(errc::not_supported, "'%s' is not a COFF object.",
+ ServerName.c_str());
+
+ Builder = std::make_unique<AppendingTypeTableBuilder>(BuilderAllocator);
+
+ // The MSVC precompiled header object file, should contain just a single
+ // ".debug$P" section.
+ COFFObjectFile &Obj = *cast<COFFObjectFile>(&BinaryObj);
+ for (const SectionRef &Section : Obj.sections()) {
+ Expected<StringRef> SectionNameOrErr = Section.getName();
+ if (!SectionNameOrErr)
+ return SectionNameOrErr.takeError();
+ if (*SectionNameOrErr == ".debug$P") {
+ Expected<StringRef> 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<BinaryStreamReader>(*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<uint8_t> TypeData = Type.data();
+ if (Type.kind() == LF_ENDPRECOMP) {
+ EndPrecompRecord EndPrecomp = cantFail(
+ TypeDeserializer::deserializeAs<EndPrecompRecord>(TypeData));
+ if (Precomp.getSignature() != EndPrecomp.getSignature())
+ return createStringError(errc::invalid_argument, "no matching pch");
+ break;
+ }
+ Builder->insertRecordBytes(TypeData);
+ }
+ // Done processing .debug$P, break out of section loop.
+ 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<uint8_t> 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<BinaryItemStream<CVType>>(llvm::support::little);
+ ItemStream->setItems(TypeArray);
+ TypeStream.setUnderlyingStream(*ItemStream);
+
+ PrecompHeader =
+ std::make_shared<LazyRandomTypeCollection>(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<StringRef> 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<TypeServer2Record>(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<PrecompRecord>(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<TpiStream &> 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<TpiStream &> 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<uint8_t> 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));
+ });
+
+ Expected<StringRef> SectionOrErr = Section.getContents();
+ if (!SectionOrErr)
+ return SectionOrErr.takeError();
+ StringRef SectionContents = *SectionOrErr;
+ StringRef Data = SectionContents;
+
+ SmallVector<StringRef, 10> SymbolNames;
+ StringMap<StringRef> 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());
+
+ // Process the subsection as normal even if the ignore bit is set.
+ 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 associated 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<COFFObjectFile>(&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<StringRef> SymNameOrErr = Object->getSymbolName(Symbol);
+ if (!SymNameOrErr) {
+ W.startLine() << "Invalid symbol name: " << Symbol.getSectionNumber()
+ << "\n";
+ consumeError(SymNameOrErr.takeError());
+ continue;
+ }
+ SymbolName = *SymNameOrErr;
+
+ LLVM_DEBUG({
+ Expected<const coff_section *> 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<StringRef> 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<StringRef> 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<std::unique_ptr<MemoryBuffer>> BuffOrErr =
+ MemoryBuffer::getFileOrSTDIN(ExePath);
+ if (BuffOrErr.getError()) {
+ return createStringError(errc::bad_file_descriptor,
+ "File '%s' does not exist.", ExePath.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<std::unique_ptr<Binary>> BinOrErr =
+ createBinary(BinaryBuffer->getMemBufferRef());
+ if (errorToErrorCode(BinOrErr.takeError())) {
+ return createStringError(errc::not_supported,
+ "Binary object format in '%s' is not supported.",
+ ExePath.c_str());
+ }
+ BinaryExecutable = std::move(*BinOrErr);
+ if (COFFObjectFile *COFFObject =
+ dyn_cast<COFFObjectFile>(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<DebugInlineeLinesSubsectionRef>(
+ Input, HeaderScope, VisitInlineeLines))
+ return Err;
+
+ // Traverse global symbols.
+ LLVM_DEBUG({ W.getOStream() << "Traversing global symbols\n"; });
+ if (Pdb.hasPDBGlobalsStream()) {
+ Expected<GlobalsStream &> GlobalsOrErr = Pdb.getPDBGlobalsStream();
+ if (!GlobalsOrErr)
+ return GlobalsOrErr.takeError();
+ GlobalsStream &Globals = *GlobalsOrErr;
+ const GSIHashTable &Table = Globals.getGlobalsTable();
+ Expected<SymbolStream &> 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; generate one using its parent name (object filename)
+ // follow by 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<CVSymbol> 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<ModuleDebugStreamRef> 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<DebugLinesSubsectionRef>(
+ 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<LVScopeCompileUnit *>(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 will:
+// = 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):
+// Create 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
+ Expected<SubtargetFeatures> Features = Obj.getFeatures();
+ SubtargetFeatures FeaturesValue;
+ if (!Features) {
+ consumeError(Features.takeError());
+ FeaturesValue = SubtargetFeatures();
+ }
+ FeaturesValue = *Features;
+ return loadGenericTargetInfo(TT.str(), FeaturesValue.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);
+}
--- /dev/null
+//===-- 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<TypeLeafKind> 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<CVType &>(CVReference), Record))
+ consumeError(std::move(Err));
+ else
+ RecordName = Record.getName();
+ };
+
+ TypeRecordKind RK = static_cast<TypeRecordKind>(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<TypeIndex>;
+
+namespace {
+
+class LVTypeRecords {
+ LVShared *Shared = nullptr;
+
+ // Logical elements associated to their CodeView Type Index.
+ using RecordEntry = std::pair<TypeLeafKind, LVElement *>;
+ using RecordTable = std::map<TypeIndex, RecordEntry>;
+ RecordTable RecordFromTypes;
+ RecordTable RecordFromIds;
+
+ using NameTable = std::map<StringRef, TypeIndex>;
+ 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, bool Create = true);
+ TypeIndex find(uint32_t StreamIdx, StringRef Name);
+};
+
+class LVForwardReferences {
+ // Forward reference and its definitions (Name as key).
+ using ForwardEntry = std::pair<TypeIndex, TypeIndex>;
+ using ForwardTypeNames = std::map<StringRef, ForwardEntry>;
+ ForwardTypeNames ForwardTypesNames;
+
+ // Forward reference and its definition (TypeIndex as key).
+ using ForwardType = std::map<TypeIndex, TypeIndex>;
+ 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<StringRef, LVScope *>;
+ Names NamespaceNames;
+
+ using LookupSet = std::set<StringRef>;
+ LookupSet DeducedScopes;
+ 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;
+ 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<uint32_t, std::string, LVScopeCompileUnit *>;
+ using StringIds = std::map<TypeIndex, StringEntry>;
+ 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<TypeLeafKind>;
+using LVSymbolKinds = std::set<SymbolKind>;
+
+// 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;
+ LVLogicalVisitor *Visitor;
+ 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, bool Create) {
+ 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 || !Create)
+ 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);
+ DeducedScopes.insert(InnerComponent);
+ if (OuterComponent.size())
+ UnresolvedScopes.insert(OuterComponent);
+}
+
+void LVNamespaceDeduction::init() {
+ // We have 2 sets of names:
+ // - deduced scopes (class, structure, union and enum) and
+ // - unresolved scopes, that can represent namespaces or any deduced.
+ // Before creating the namespaces, we have to traverse the unresolved
+ // and remove any references to already deduced scopes.
+ LVStringRefs Components;
+ for (const StringRef &Unresolved : UnresolvedScopes) {
+ Components = getAllLexicalComponents(Unresolved);
+ for (const StringRef &Component : Components) {
+ LookupSet::iterator Iter = DeducedScopes.find(Component);
+ if (Iter == DeducedScopes.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)
+ dbgs() << formatv("'{0}'\n", Item.str().c_str());
+ };
+
+ Print(DeducedScopes, "Deducted Scopes");
+ Print(UnresolvedScopes, "Unresolved Scopes");
+ Print(IdentifiedNamespaces, "Namespaces");
+ });
+}
+
+LVScope *LVNamespaceDeduction::get(LVStringRefs Components) {
+ LLVM_DEBUG({
+ for (const StringRef &Component : Components)
+ dbgs() << formatv("'{0}'\n", Component.str().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 = Shared->Reader->createScopeNamespace();
+ 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(
+ { dbgs() << formatv("ScopedName: '{0}'\n", ScopedName.str().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<uint32_t>(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();
+}
+
+#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<StringRef> 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<LocalVariableAddrGap> 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);
+
+ LogicalVisitor->CurrentElement = LogicalVisitor->createElement(Kind);
+ if (!LogicalVisitor->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 (!LogicalVisitor->CurrentElement->getOffsetFromTypeIndex())
+ LogicalVisitor->CurrentElement->setOffset(Offset);
+ if (symbolOpensScope(Kind) || (IsCompileUnit = symbolIsCompileUnit(Kind))) {
+ assert(LogicalVisitor->CurrentScope && "Invalid scope!");
+ LogicalVisitor->addElement(LogicalVisitor->CurrentScope, IsCompileUnit);
+ } else {
+ if (LogicalVisitor->CurrentSymbol)
+ LogicalVisitor->addElement(LogicalVisitor->CurrentSymbol);
+ if (LogicalVisitor->CurrentType)
+ LogicalVisitor->addElement(LogicalVisitor->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 = LogicalVisitor->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 = LogicalVisitor->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 = LogicalVisitor->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 = LogicalVisitor->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 = LogicalVisitor->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 = LogicalVisitor->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;
+
+ LocalVariableAddrRange Range = DefRangeRegister.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_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;
+
+ LocalVariableAddrRange Range = DefRangeSubfieldRegister.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_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<CodeViewError>(
+ "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<CodeViewError>(
+ "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 = LogicalVisitor->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 = LogicalVisitor->CurrentScope) {
+ LVScope *AbstractFunction = Reader->createScopeFunction();
+ 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 = LogicalVisitor->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<CodeViewError>("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<uint8_t>(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 = LogicalVisitor->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);
+
+ std::optional<CVType> 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<CodeViewError>("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();
+ } else {
+ // Clang generates global ctor and dtor names containing the substrings:
+ // 'dynamic initializer for' and 'dynamic atexit destructor for'.
+ if (DemangledSymbol.find("dynamic atexit destructor for") !=
+ 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<CodeViewError>("Visiting a Thunk32Sym while inside "
+ "function scope!");
+
+ InFunctionScope = true;
+
+ LLVM_DEBUG({
+ W.printHex("Segment", Thunk.Segment);
+ W.printString("Name", Thunk.Name);
+ });
+
+ if (LVScope *Function = LogicalVisitor->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 = LogicalVisitor->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 = std::make_shared<LVShared>(Reader, this);
+}
+
+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()), ArrayRef(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), ArrayRef(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<TypeIndex> 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<LVScope *>(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<LVScopeArray *>(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 = Reader->createTypeSubrange();
+ 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<CVType &>(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<uint32_t>(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<LVScopeAggregate *>(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<TypeRecordKind>(CVReference.kind());
+ ClassRecord ReferenceRecord(RK);
+ if (Error Err = TypeDeserializer::deserializeAs(
+ const_cast<CVType &>(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<LVScopeEnumeration *>(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<LVScope *>(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<LVScope *>(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<LVScope *>(
+ 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<LVScope *>(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 incoming 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<LVType *>(Element);
+ if (!LastLink->getParentScope())
+ CompileUnit->addElement(LastLink);
+
+ bool SeenModifier = false;
+ uint16_t Mods = static_cast<uint16_t>(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 = Reader->createType();
+ 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 = Reader->createType();
+ 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);
+ });
+
+ // Find the pointed-to type.
+ LVType *Pointer = static_cast<LVType *>(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:
+ // <restrict> <pointer, Reference, ValueReference> <const, volatile>
+ // Const and volatile already processed.
+ bool SeenModifier = false;
+ LVType *LastLink = Pointer;
+ if (!LastLink->getParentScope())
+ CompileUnit->addElement(LastLink);
+
+ if (Ptr.isRestrict()) {
+ SeenModifier = true;
+ LVType *Restrict = Reader->createType();
+ 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 = Reader->createType();
+ 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 = Reader->createType();
+ 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<LVScope *>(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<LVScopeAggregate *>(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<TypeIndex> 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<LVScope *>(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<LVScope *>(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<LVScope *>(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<LVScope *>(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<LVScope *>(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<LVScope *>(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<LVScope *>(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<Name##Record>(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<Name##Record>(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<uint8_t> 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 = Reader->createType();
+ CurrentType->setIsBase();
+ CurrentType->setTag(dwarf::DW_TAG_base_type);
+ if (options().getAttributeBase())
+ CurrentType->setIncludeInPrint();
+ return CurrentType;
+ }
+
+ switch (Kind) {
+ // Types.
+ case TypeLeafKind::LF_ENUMERATE:
+ CurrentType = Reader->createTypeEnumerator();
+ CurrentType->setTag(dwarf::DW_TAG_enumerator);
+ return CurrentType;
+ case TypeLeafKind::LF_MODIFIER:
+ CurrentType = Reader->createType();
+ CurrentType->setIsModifier();
+ return CurrentType;
+ case TypeLeafKind::LF_POINTER:
+ CurrentType = Reader->createType();
+ 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 = Reader->createSymbol();
+ CurrentSymbol->setTag(dwarf::DW_TAG_inheritance);
+ CurrentSymbol->setIsInheritance();
+ return CurrentSymbol;
+ case TypeLeafKind::LF_MEMBER:
+ case TypeLeafKind::LF_STMEMBER:
+ CurrentSymbol = Reader->createSymbol();
+ CurrentSymbol->setIsMember();
+ CurrentSymbol->setTag(dwarf::DW_TAG_member);
+ return CurrentSymbol;
+
+ // Scopes.
+ case TypeLeafKind::LF_ARRAY:
+ CurrentScope = Reader->createScopeArray();
+ CurrentScope->setTag(dwarf::DW_TAG_array_type);
+ return CurrentScope;
+ case TypeLeafKind::LF_CLASS:
+ CurrentScope = Reader->createScopeAggregate();
+ CurrentScope->setTag(dwarf::DW_TAG_class_type);
+ CurrentScope->setIsClass();
+ return CurrentScope;
+ case TypeLeafKind::LF_ENUM:
+ CurrentScope = Reader->createScopeEnumeration();
+ CurrentScope->setTag(dwarf::DW_TAG_enumeration_type);
+ return CurrentScope;
+ case TypeLeafKind::LF_METHOD:
+ case TypeLeafKind::LF_ONEMETHOD:
+ case TypeLeafKind::LF_PROCEDURE:
+ CurrentScope = Reader->createScopeFunction();
+ CurrentScope->setIsSubprogram();
+ CurrentScope->setTag(dwarf::DW_TAG_subprogram);
+ return CurrentScope;
+ case TypeLeafKind::LF_STRUCTURE:
+ CurrentScope = Reader->createScopeAggregate();
+ CurrentScope->setIsStructure();
+ CurrentScope->setTag(dwarf::DW_TAG_structure_type);
+ return CurrentScope;
+ case TypeLeafKind::LF_UNION:
+ CurrentScope = Reader->createScopeAggregate();
+ 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 = Reader->createTypeDefinition();
+ CurrentType->setTag(dwarf::DW_TAG_typedef);
+ return CurrentType;
+
+ // Symbols.
+ case SymbolKind::S_CONSTANT:
+ CurrentSymbol = Reader->createSymbol();
+ 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 = Reader->createSymbol();
+ CurrentSymbol->setIsVariable();
+ CurrentSymbol->setTag(dwarf::DW_TAG_variable);
+ return CurrentSymbol;
+
+ // Scopes.
+ case SymbolKind::S_BLOCK32:
+ CurrentScope = Reader->createScope();
+ CurrentScope->setIsLexicalBlock();
+ CurrentScope->setTag(dwarf::DW_TAG_lexical_block);
+ return CurrentScope;
+ case SymbolKind::S_COMPILE2:
+ case SymbolKind::S_COMPILE3:
+ CurrentScope = Reader->createScopeCompileUnit();
+ CurrentScope->setTag(dwarf::DW_TAG_compile_unit);
+ Reader->setCompileUnit(static_cast<LVScopeCompileUnit *>(CurrentScope));
+ return CurrentScope;
+ case SymbolKind::S_INLINESITE:
+ case SymbolKind::S_INLINESITE2:
+ CurrentScope = Reader->createScopeFunctionInlined();
+ 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 = Reader->createScopeFunction();
+ 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 = Reader->createSymbol();
+ 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<LVType *>(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<LVType *>(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<LVScope *>(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<CVType &>(CVRecord), Line))
+ consumeError(std::move(Err));
+ else {
+ LLVM_DEBUG({
+ printTypeIndex("UDT", Line.getUDT(), StreamIPI);
+ printTypeIndex("SourceFile", Line.getSourceFile(), StreamIPI);
+ W.printNumber("LineNumber", Line.getLineNumber());
+ });
+
+ // The TypeIndex returned by 'getUDT()' must point to an already
+ // created logical element. If no logical element is found, it means
+ // the LF_UDT_SRC_LINE is associated with a system TypeIndex.
+ if (LVElement *Element = Shared->TypeRecords.find(
+ StreamTPI, Line.getUDT(), /*Create=*/false)) {
+ 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";
+ }
+ };
+ OS << format("%20s", Name.str().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()) {
+ if (!Locations->empty())
+ ParentLowPC = (*Locations->begin())->getLowerAddress();
+ }
+
+ // For the given inlinesite, get the initial line number and its
+ // 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";
+ });
+
+ // Get the source lines if requested by command line option.
+ if (!options().getPrintLines())
+ return Error::success();
+
+ // 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 = Reader->createLineDebug();
+ 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;
+ 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();
+}
}
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 {
--- /dev/null
+; REQUIRES: x86-registered-target
+
+; 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
--- /dev/null
+; REQUIRES: x86-registered-target
+
+; 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'
--- /dev/null
+; REQUIRES: x86-registered-target
+
+; 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'
--- /dev/null
+; REQUIRES: x86-registered-target
+
+; 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'
--- /dev/null
+; REQUIRES: x86-registered-target
+
+; 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'
--- /dev/null
+; REQUIRES: x86-registered-target
+
+; 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 CodeView debug
+; information generated by the Clang compiler does not include any
+; references to the enumerators 'RED' and 'BLUE'. The CodeView generated
+; by GCC and MSVC, 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
--- /dev/null
+; REQUIRES: x86-registered-target
+
+; 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 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'
--- /dev/null
+; REQUIRES: x86-registered-target
+
+; 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:
set(LLVM_LINK_COMPONENTS
AllTargetsDescs
- AllTargetsInfos
AllTargetsDisassemblers
+ AllTargetsInfos
DebugInfoLogicalView
MC
MCDisassembler
)
add_llvm_unittest_with_input_files(DebugInfoLogicalViewTests
+ CodeViewReaderTest.cpp
CommandLineOptionsTest.cpp
CompareElementsTest.cpp
ELFReaderTest.cpp
--- /dev/null
+//===- 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/MC/TargetRegistry.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(), 1u);
+
+ LVScopes::const_iterator Iter = Scopes->begin();
+ LVScope *Child = *Iter;
+ EXPECT_NE(Child, nullptr);
+ return Child;
+}
+
+// Helper function to create a reader.
+std::unique_ptr<LVReader> createReader(LVReaderHandler &ReaderHandler,
+ SmallString<128> &InputsDir,
+ StringRef Filename) {
+ SmallString<128> ObjectName(InputsDir);
+ llvm::sys::path::append(ObjectName, Filename);
+
+ Expected<std::unique_ptr<LVReader>> ReaderOrErr =
+ ReaderHandler.createReader(std::string(ObjectName));
+ EXPECT_THAT_EXPECTED(ReaderOrErr, Succeeded());
+ std::unique_ptr<LVReader> Reader = std::move(*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<LVScopeCompileUnit *>(getFirstScopeChild(Root));
+ LVScopeFunction *Function =
+ static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit));
+
+ EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
+ EXPECT_EQ(Root->getName(), CodeViewClang);
+
+ EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
+ EXPECT_TRUE(CompileUnit->getProducer().startswith("clang"));
+ EXPECT_EQ(CompileUnit->getName(), "test.cpp");
+
+ EXPECT_EQ(Function->lineCount(), 16u);
+ EXPECT_EQ(Function->scopeCount(), 1u);
+ EXPECT_EQ(Function->symbolCount(), 3u);
+ EXPECT_EQ(Function->typeCount(), 1u);
+ EXPECT_EQ(Function->rangeCount(), 1u);
+
+ const LVLocations *Ranges = Function->getRanges();
+ ASSERT_NE(Ranges, nullptr);
+ ASSERT_EQ(Ranges->size(), 1u);
+ 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(), 2u);
+ LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
+ LVRangeEntry RangeEntry = *IterRanges;
+ EXPECT_EQ(RangeEntry.lower(), 0u);
+ EXPECT_EQ(RangeEntry.upper(), 0x46u);
+ EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+ EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
+ EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+ ++IterRanges;
+ RangeEntry = *IterRanges;
+ EXPECT_EQ(RangeEntry.lower(), 0x21u);
+ EXPECT_EQ(RangeEntry.upper(), 0x35u);
+ EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+ EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
+ EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+ const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
+ ASSERT_EQ(PublicNames.size(), 1u);
+ LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
+ LVScope *Foo = (*IterNames).first;
+ EXPECT_EQ(Foo->getName(), "foo");
+ EXPECT_EQ(Foo->getLineNumber(), 0u);
+ LVNameInfo NameInfo = (*IterNames).second;
+ EXPECT_EQ(NameInfo.first, 0u);
+ EXPECT_EQ(NameInfo.second, 0x46u);
+
+ // Lines (debug and assembler) for 'foo'.
+ const LVLines *Lines = Foo->getLines();
+ ASSERT_NE(Lines, nullptr);
+ EXPECT_EQ(Lines->size(), 0x10u);
+}
+
+// Check the logical elements basic properties (MSVC - Codeview).
+void checkElementPropertiesMsvcCodeview(LVReader *Reader) {
+ LVScopeRoot *Root = Reader->getScopesRoot();
+ LVScopeCompileUnit *CompileUnit =
+ static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root));
+ LVScopeFunction *Function =
+ static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit));
+
+ EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
+ EXPECT_EQ(Root->getName(), CodeViewMsvc);
+
+ EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
+ EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft"));
+ EXPECT_EQ(CompileUnit->getName(), "test.cpp");
+
+ EXPECT_EQ(Function->lineCount(), 14u);
+ EXPECT_EQ(Function->scopeCount(), 1u);
+ EXPECT_EQ(Function->symbolCount(), 3u);
+ EXPECT_EQ(Function->typeCount(), 0u);
+ EXPECT_EQ(Function->rangeCount(), 1u);
+
+ const LVLocations *Ranges = Function->getRanges();
+ ASSERT_NE(Ranges, nullptr);
+ ASSERT_EQ(Ranges->size(), 1u);
+ 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(), 2u);
+ LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
+ LVRangeEntry RangeEntry = *IterRanges;
+ EXPECT_EQ(RangeEntry.lower(), 0u);
+ EXPECT_EQ(RangeEntry.upper(), 0x31u);
+ EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+ EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
+ EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+ ++IterRanges;
+ RangeEntry = *IterRanges;
+ EXPECT_EQ(RangeEntry.lower(), 0x1bu);
+ EXPECT_EQ(RangeEntry.upper(), 0x28u);
+ EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+ EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
+ EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+ const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
+ ASSERT_EQ(PublicNames.size(), 1u);
+ LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
+ LVScope *Foo = (*IterNames).first;
+ EXPECT_EQ(Foo->getName(), "foo");
+ EXPECT_EQ(Foo->getLineNumber(), 0u);
+ LVNameInfo NameInfo = (*IterNames).second;
+ EXPECT_EQ(NameInfo.first, 0u);
+ EXPECT_EQ(NameInfo.second, 0x31u);
+
+ // Lines (debug and assembler) for 'foo'.
+ const LVLines *Lines = Foo->getLines();
+ ASSERT_NE(Lines, nullptr);
+ EXPECT_EQ(Lines->size(), 0x0eu);
+}
+
+// Check the logical elements basic properties (MSVC - PDB).
+void checkElementPropertiesMsvcCodeviewPdb(LVReader *Reader) {
+ LVScopeRoot *Root = Reader->getScopesRoot();
+ LVScopeCompileUnit *CompileUnit =
+ static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root));
+ LVScopeFunction *Function =
+ static_cast<LVScopeFunction *>(getFirstScopeChild(CompileUnit));
+
+ EXPECT_EQ(Root->getFileFormatName(), "COFF-x86-64");
+ EXPECT_EQ(Root->getName(), CodeViewPdbMsvc);
+
+ EXPECT_EQ(CompileUnit->getBaseAddress(), 0u);
+ EXPECT_TRUE(CompileUnit->getProducer().startswith("Microsoft"));
+ EXPECT_EQ(CompileUnit->getName(), "test.cpp");
+
+ EXPECT_EQ(Function->lineCount(), 14u);
+ EXPECT_EQ(Function->scopeCount(), 1u);
+ EXPECT_EQ(Function->symbolCount(), 3u);
+ EXPECT_EQ(Function->typeCount(), 0u);
+ EXPECT_EQ(Function->rangeCount(), 1u);
+
+ const LVLocations *Ranges = Function->getRanges();
+ ASSERT_NE(Ranges, nullptr);
+ ASSERT_EQ(Ranges->size(), 1u);
+ 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(), 2u);
+ LVRangeEntries::const_iterator IterRanges = RangeEntries.cbegin();
+ LVRangeEntry RangeEntry = *IterRanges;
+ EXPECT_EQ(RangeEntry.lower(), 0u);
+ EXPECT_EQ(RangeEntry.upper(), 0x31u);
+ EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+ EXPECT_EQ(RangeEntry.scope()->getName(), "foo");
+ EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+ ++IterRanges;
+ RangeEntry = *IterRanges;
+ EXPECT_EQ(RangeEntry.lower(), 0x1bu);
+ EXPECT_EQ(RangeEntry.upper(), 0x28u);
+ EXPECT_EQ(RangeEntry.scope()->getLineNumber(), 0u);
+ EXPECT_EQ(RangeEntry.scope()->getName(), "foo::?");
+ EXPECT_EQ(RangeEntry.scope()->getOffset(), 0u);
+
+ const LVPublicNames &PublicNames = CompileUnit->getPublicNames();
+ ASSERT_EQ(PublicNames.size(), 1u);
+ LVPublicNames::const_iterator IterNames = PublicNames.cbegin();
+ LVScope *Foo = (*IterNames).first;
+ EXPECT_EQ(Foo->getName(), "foo");
+ EXPECT_EQ(Foo->getLineNumber(), 0u);
+ LVNameInfo NameInfo = (*IterNames).second;
+ EXPECT_EQ(NameInfo.first, 0u);
+ EXPECT_EQ(NameInfo.second, 0x31u);
+
+ // Lines (debug and assembler) for 'foo'.
+ const LVLines *Lines = Foo->getLines();
+ ASSERT_NE(Lines, nullptr);
+ EXPECT_EQ(Lines->size(), 0x0eu);
+}
+
+struct SelectionInfo {
+ const char *Name;
+ LVElementGetFunction Function;
+};
+
+// Check the logical elements selection.
+void checkElementSelection(LVReader *Reader, std::vector<SelectionInfo> &Data,
+ size_t Size) {
+ LVScopeRoot *Root = Reader->getScopesRoot();
+ LVScopeCompileUnit *CompileUnit =
+ static_cast<LVScopeCompileUnit *>(getFirstScopeChild(Root));
+
+ // Get the matched elements.
+ LVElements MatchedElements = CompileUnit->getMatchedElements();
+ std::map<StringRef, LVElement *> MapElements;
+ for (LVElement *Element : MatchedElements)
+ MapElements[Element->getName()] = Element;
+ ASSERT_EQ(MapElements.size(), Size);
+
+ std::map<StringRef, LVElement *>::iterator Iter = MapElements.begin();
+ for (const SelectionInfo &Entry : Data) {
+ // Get matched element.
+ EXPECT_NE(Iter, MapElements.end());
+ LVElement *Element = Iter->second;
+ ASSERT_NE(Element, nullptr);
+ EXPECT_NE(Element->getName().find(Entry.Name), StringRef::npos);
+ EXPECT_EQ((Element->*Entry.Function)(), 1u);
+ ++Iter;
+ }
+
+ // Get the parents for the matched elements.
+ LVScopes MatchedScopes = CompileUnit->getMatchedScopes();
+ std::set<StringRef> SetScopes;
+ for (LVScope *Scope : MatchedScopes)
+ SetScopes.insert(Scope->getName());
+ ASSERT_EQ(SetScopes.size(), 3u);
+
+ // Parents of selected elements.
+ std::set<StringRef>::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(), 2u);
+
+ LVReader *Reader;
+ LVElement *Element;
+ LVComparePass Pass;
+
+ // Reference: Missing TypeDefinition 'INTEGER'
+ std::tie(Reader, Element, Pass) = PassTable[0];
+ ASSERT_NE(Reader, nullptr);
+ ASSERT_NE(Element, nullptr);
+ EXPECT_EQ(Reader, Reference);
+ EXPECT_EQ(Element->getLevel(), 3u);
+ EXPECT_EQ(Element->getLineNumber(), 0u);
+ EXPECT_EQ(Element->getName(), "INTEGER");
+ EXPECT_EQ(Pass, LVComparePass::Missing);
+
+ // Target: Added TypeDefinition 'INTEGER'
+ std::tie(Reader, Element, Pass) = PassTable[1];
+ ASSERT_NE(Reader, nullptr);
+ ASSERT_NE(Element, nullptr);
+ EXPECT_EQ(Reader, Target);
+ EXPECT_EQ(Element->getLevel(), 4u);
+ EXPECT_EQ(Element->getLineNumber(), 0u);
+ 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<std::string> Objects;
+ ScopedPrinter W(outs());
+ LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
+
+ // Check logical elements properties.
+ {
+ std::unique_ptr<LVReader> Reader =
+ createReader(ReaderHandler, InputsDir, CodeViewClang);
+ checkElementPropertiesClangCodeview(Reader.get());
+ }
+ {
+ std::unique_ptr<LVReader> Reader =
+ createReader(ReaderHandler, InputsDir, CodeViewMsvc);
+ checkElementPropertiesMsvcCodeview(Reader.get());
+ }
+ {
+ std::unique_ptr<LVReader> Reader =
+ createReader(ReaderHandler, InputsDir, CodeViewPdbMsvc);
+ checkElementPropertiesMsvcCodeviewPdb(Reader.get());
+ }
+}
+
+// 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<std::string> Objects;
+ ScopedPrinter W(outs());
+ LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
+
+ // Check logical elements selection.
+ {
+ std::vector<SelectionInfo> 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}};
+ std::unique_ptr<LVReader> Reader =
+ createReader(ReaderHandler, InputsDir, CodeViewClang);
+ checkElementSelection(Reader.get(), DataClang, DataClang.size());
+ }
+ {
+ std::vector<SelectionInfo> 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}};
+ std::unique_ptr<LVReader> Reader =
+ createReader(ReaderHandler, InputsDir, CodeViewMsvc);
+ checkElementSelection(Reader.get(), DataMsvc, DataMsvc.size());
+ }
+}
+
+// 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<std::string> Objects;
+ ScopedPrinter W(outs());
+ LVReaderHandler ReaderHandler(Objects, W, ReaderOptions);
+
+ // Check logical comparison.
+ std::unique_ptr<LVReader> Reference =
+ createReader(ReaderHandler, InputsDir, CodeViewClang);
+ std::unique_ptr<LVReader> Target =
+ createReader(ReaderHandler, InputsDir, CodeViewMsvc);
+ checkElementComparison(Reference.get(), Target.get());
+}
+
+TEST(LogicalViewTest, CodeViewReader) {
+ // Initialize targets and assembly printers/parsers.
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ InitializeAllDisassemblers();
+
+ llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
+
+ // This test requires a x86-registered-target.
+ Triple TT;
+ TT.setArch(Triple::x86_64);
+ TT.setVendor(Triple::UnknownVendor);
+ TT.setOS(Triple::UnknownOS);
+
+ std::string TargetLookupError;
+ if (!TargetRegistry::lookupTarget(std::string(TT.str()), TargetLookupError))
+ return;
+
+ SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);
+
+ // Logical elements general properties and selection.
+ elementProperties(InputsDir);
+ elementSelection(InputsDir);
+
+ // Compare logical elements.
+ compareElements(InputsDir);
+}
+
+} // namespace