From 2f7efbc9ce62d84a215529133f1c384c25a7858e Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Tue, 23 Oct 2018 16:37:53 +0000 Subject: [PATCH] [NativePDB] Add basic support for tag types to the native pdb plugin. This adds support to LLDB for named types (class, struct, union, and enum). This is true cross platform support, and hits the PDB file directly without a dependency on Windows. Tests are added which compile a program with certain interesting types and then use load the target in LLDB and use "type lookup -- " to dump the layout of the type in LLDB without a running process. Currently only fields are parsed -- we do not parse methods. Also we don't deal with bitfields or virtual bases correctly. Those will make good followups. Differential Revision: https://reviews.llvm.org/D53511 llvm-svn: 345047 --- .../SymbolFile/NativePDB/Inputs/tag-types.lldbinit | 8 + lldb/lit/SymbolFile/NativePDB/tag-types.cpp | 236 +++++++ .../Plugins/SymbolFile/NativePDB/CMakeLists.txt | 1 + .../Plugins/SymbolFile/NativePDB/PdbIndex.cpp | 6 + .../SymbolFile/NativePDB/SymbolFileNativePDB.cpp | 700 ++++++++++++++++++++- .../SymbolFile/NativePDB/SymbolFileNativePDB.h | 42 ++ .../SymbolFile/NativePDB/UdtRecordCompleter.cpp | 188 ++++++ .../SymbolFile/NativePDB/UdtRecordCompleter.h | 68 ++ 8 files changed, 1246 insertions(+), 3 deletions(-) create mode 100644 lldb/lit/SymbolFile/NativePDB/Inputs/tag-types.lldbinit create mode 100644 lldb/lit/SymbolFile/NativePDB/tag-types.cpp create mode 100644 lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp create mode 100644 lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h diff --git a/lldb/lit/SymbolFile/NativePDB/Inputs/tag-types.lldbinit b/lldb/lit/SymbolFile/NativePDB/Inputs/tag-types.lldbinit new file mode 100644 index 0000000..c50e181 --- /dev/null +++ b/lldb/lit/SymbolFile/NativePDB/Inputs/tag-types.lldbinit @@ -0,0 +1,8 @@ +type lookup -- Struct +type lookup -- Class +type lookup -- Union +type lookup -- Derived +type lookup -- Derived2 +type lookup -- EnumInt +type lookup -- EnumShort +type lookup -- InvalidType diff --git a/lldb/lit/SymbolFile/NativePDB/tag-types.cpp b/lldb/lit/SymbolFile/NativePDB/tag-types.cpp new file mode 100644 index 0000000..ce28bb9 --- /dev/null +++ b/lldb/lit/SymbolFile/NativePDB/tag-types.cpp @@ -0,0 +1,236 @@ +// clang-format off +// REQUIRES: lld + +// Test that we can display tag types. +// RUN: clang-cl /Z7 /GS- /GR- /c /Fo%t.obj -- %s +// RUN: lld-link /DEBUG /nodefaultlib /entry:main /OUT:%t.exe /PDB:%t.pdb -- %t.obj +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb -f %t.exe -s \ +// RUN: %p/Inputs/tag-types.lldbinit | FileCheck %s + +// Test struct +struct Struct { + // Test builtin types, which are represented by special CodeView type indices. + bool B; + char C; + signed char SC; + unsigned char UC; + char16_t C16; + char32_t C32; + wchar_t WC; + short S; + unsigned short US; + int I; + unsigned int UI; + long L; + unsigned long UL; + long long LL; + unsigned long long ULL; + float F; + double D; + long double LD; +}; + +// Test class +class Class { +public: + // Test pointers to builtin types, which are represented by different special + // CodeView type indices. + bool *PB; + char *PC; + signed char *PSC; + unsigned char *PUC; + char16_t *PC16; + char32_t *PC32; + wchar_t *PWC; + short *PS; + unsigned short *PUS; + int *PI; + unsigned int *PUI; + long *PL; + unsigned long *PUL; + long long *PLL; + unsigned long long *PULL; + float *PF; + double *PD; + long double *PLD; +}; + +// Test union +union Union { + // Test modified types. + const bool *PB; + const char *PC; + const signed char *PSC; + const unsigned char *PUC; + const char16_t *PC16; + const char32_t *PC32; + const wchar_t *PWC; + const short *PS; + const unsigned short *PUS; + const int *PI; + const unsigned int *PUI; + const long *PL; + const unsigned long *PUL; + const long long *PLL; + const unsigned long long *PULL; + const float *PF; + const double *PD; + const long double *PLD; +}; + +struct OneMember { + int N = 0; +}; + + +// Test single inheritance. +class Derived : public Class { +public: + explicit Derived() + : Reference(*this), RefMember(Member), RValueRefMember((OneMember&&)Member) {} + + // Test reference to self, to make sure we don't end up in an + // infinite cycle. + Derived &Reference; + + // Test aggregate class member. + OneMember Member; + + // And modified aggregate class member. + const OneMember ConstMember; + volatile OneMember VolatileMember; + const volatile OneMember CVMember; + + // And all types of pointers to class members + OneMember *PtrMember; + OneMember &RefMember; + OneMember &&RValueRefMember; +}; + +// Test multiple inheritance, as well as protected and private inheritance. +class Derived2 : protected Class, private Struct { +public: + // Test static data members + static unsigned StaticDataMember; +}; + +unsigned Derived2::StaticDataMember = 0; + +// Test scoped enums and unscoped enums. +enum class EnumInt { + A = 1, + B = 2 +}; + +// Test explicit underlying types +enum EnumShort : short { + ES_A = 2, + ES_B = 3 +}; + +int main(int argc, char **argv) { + Struct S; + Class C; + Union U; + Derived D; + Derived2 D2; + EnumInt EI; + EnumShort ES; + + return 0; +} + +// CHECK: (lldb) target create "{{.*}}tag-types.cpp.tmp.exe" +// CHECK-NEXT: Current executable set to '{{.*}}tag-types.cpp.tmp.exe' (x86_64). +// CHECK-NEXT: (lldb) command source -s 0 '{{.*}}tag-types.lldbinit' +// CHECK-NEXT: Executing commands in '{{.*}}tag-types.lldbinit'. +// CHECK-NEXT: (lldb) type lookup -- Struct +// CHECK-NEXT: struct Struct { +// CHECK-NEXT: bool B; +// CHECK-NEXT: char C; +// CHECK-NEXT: signed char SC; +// CHECK-NEXT: unsigned char UC; +// CHECK-NEXT: char16_t C16; +// CHECK-NEXT: char32_t C32; +// CHECK-NEXT: wchar_t WC; +// CHECK-NEXT: short S; +// CHECK-NEXT: unsigned short US; +// CHECK-NEXT: int I; +// CHECK-NEXT: unsigned int UI; +// CHECK-NEXT: long L; +// CHECK-NEXT: unsigned long UL; +// CHECK-NEXT: long long LL; +// CHECK-NEXT: unsigned long long ULL; +// CHECK-NEXT: float F; +// CHECK-NEXT: double D; +// CHECK-NEXT: double LD; +// CHECK-NEXT: } +// CHECK-NEXT: (lldb) type lookup -- Class +// CHECK-NEXT: class Class { +// CHECK-NEXT: bool *PB; +// CHECK-NEXT: char *PC; +// CHECK-NEXT: signed char *PSC; +// CHECK-NEXT: unsigned char *PUC; +// CHECK-NEXT: char16_t *PC16; +// CHECK-NEXT: char32_t *PC32; +// CHECK-NEXT: wchar_t *PWC; +// CHECK-NEXT: short *PS; +// CHECK-NEXT: unsigned short *PUS; +// CHECK-NEXT: int *PI; +// CHECK-NEXT: unsigned int *PUI; +// CHECK-NEXT: long *PL; +// CHECK-NEXT: unsigned long *PUL; +// CHECK-NEXT: long long *PLL; +// CHECK-NEXT: unsigned long long *PULL; +// CHECK-NEXT: float *PF; +// CHECK-NEXT: double *PD; +// CHECK-NEXT: double *PLD; +// CHECK-NEXT: } +// CHECK-NEXT: (lldb) type lookup -- Union +// CHECK-NEXT: union Union { +// CHECK-NEXT: const bool *PB; +// CHECK-NEXT: const char *PC; +// CHECK-NEXT: const signed char *PSC; +// CHECK-NEXT: const unsigned char *PUC; +// CHECK-NEXT: const char16_t *PC16; +// CHECK-NEXT: const char32_t *PC32; +// CHECK-NEXT: const wchar_t *PWC; +// CHECK-NEXT: const short *PS; +// CHECK-NEXT: const unsigned short *PUS; +// CHECK-NEXT: const int *PI; +// CHECK-NEXT: const unsigned int *PUI; +// CHECK-NEXT: const long *PL; +// CHECK-NEXT: const unsigned long *PUL; +// CHECK-NEXT: const long long *PLL; +// CHECK-NEXT: const unsigned long long *PULL; +// CHECK-NEXT: const float *PF; +// CHECK-NEXT: const double *PD; +// CHECK-NEXT: const double *PLD; +// CHECK-NEXT: } +// CHECK-NEXT: (lldb) type lookup -- Derived +// CHECK-NEXT: class Derived : public Class { +// CHECK-NEXT: Derived &Reference; +// CHECK-NEXT: OneMember Member; +// CHECK-NEXT: const OneMember ConstMember; +// CHECK-NEXT: volatile OneMember VolatileMember; +// CHECK-NEXT: const volatile OneMember CVMember; +// CHECK-NEXT: OneMember *PtrMember; +// CHECK-NEXT: OneMember &RefMember; +// CHECK-NEXT: OneMember &&RValueRefMember; +// CHECK-NEXT: } +// CHECK-NEXT: (lldb) type lookup -- Derived2 +// CHECK-NEXT: class Derived2 : protected Class, private Struct { +// CHECK-NEXT: static unsigned int StaticDataMember; +// CHECK-NEXT: } +// CHECK-NEXT: (lldb) type lookup -- EnumInt +// CHECK-NEXT: enum EnumInt { +// CHECK-NEXT: A, +// CHECK-NEXT: B +// CHECK-NEXT: } +// CHECK-NEXT: (lldb) type lookup -- EnumShort +// CHECK-NEXT: enum EnumShort { +// CHECK-NEXT: ES_A, +// CHECK-NEXT: ES_B +// CHECK-NEXT: } +// CHECK-NEXT: (lldb) type lookup -- InvalidType +// CHECK-NEXT: no type was found matching 'InvalidType' diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt b/lldb/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt index dc459ec..c463cd8 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt +++ b/lldb/source/Plugins/SymbolFile/NativePDB/CMakeLists.txt @@ -3,6 +3,7 @@ add_lldb_library(lldbPluginSymbolFileNativePDB PLUGIN PdbIndex.cpp PdbUtil.cpp SymbolFileNativePDB.cpp + UdtRecordCompleter.cpp LINK_LIBS clangAST diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp index c9bd9a4..cda9508 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbIndex.cpp @@ -17,6 +17,7 @@ #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Error.h" @@ -51,6 +52,8 @@ PdbIndex::create(std::unique_ptr file) { ASSIGN_PTR_OR_RETURN(result->m_globals, file->getPDBGlobalsStream()); ASSIGN_PTR_OR_RETURN(result->m_symrecords, file->getPDBSymbolStream()); + result->m_tpi->buildHashMap(); + result->m_file = std::move(file); return std::move(result); @@ -101,6 +104,9 @@ void PdbIndex::ParseSectionContribs() { : m_ctx(ctx), m_imap(imap) {} void visit(const SectionContrib &C) override { + if (C.Size == 0) + return; + uint64_t va = m_ctx.MakeVirtualAddress(C.ISect, C.Off); uint64_t end = va + C.Size; // IntervalMap's start and end represent a closed range, not a half-open diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index 748c0de..9aa5ecb 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -9,8 +9,16 @@ #include "SymbolFileNativePDB.h" +#include "clang/AST/Attr.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" + #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/ObjectFile.h" @@ -18,15 +26,19 @@ #include "lldb/Symbol/SymbolVendor.h" #include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeDeserializer.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/ModuleDebugStream.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" #include "llvm/Object/COFF.h" #include "llvm/Support/Allocator.h" @@ -36,10 +48,11 @@ #include "PdbSymUid.h" #include "PdbUtil.h" +#include "UdtRecordCompleter.h" using namespace lldb; using namespace lldb_private; -using namespace lldb_private::npdb; +using namespace npdb; using namespace llvm::codeview; using namespace llvm::pdb; @@ -139,6 +152,265 @@ static bool IsFunctionEpilogue(const CompilandIndexItem &cci, return false; } +static clang::MSInheritanceAttr::Spelling +GetMSInheritance(LazyRandomTypeCollection &tpi, const ClassRecord &record) { + if (record.DerivationList == TypeIndex::None()) + return clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance; + + CVType bases = tpi.getType(record.DerivationList); + ArgListRecord base_list; + cantFail(TypeDeserializer::deserializeAs(bases, base_list)); + if (base_list.ArgIndices.empty()) + return clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance; + + int base_count = 0; + for (TypeIndex ti : base_list.ArgIndices) { + CVType base = tpi.getType(ti); + if (base.kind() == LF_VBCLASS || base.kind() == LF_IVBCLASS) + return clang::MSInheritanceAttr::Spelling::Keyword_virtual_inheritance; + ++base_count; + } + + if (base_count > 1) + return clang::MSInheritanceAttr::Keyword_multiple_inheritance; + return clang::MSInheritanceAttr::Keyword_single_inheritance; +} + +static lldb::BasicType GetCompilerTypeForSimpleKind(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Boolean8: + return lldb::eBasicTypeBool; + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + return lldb::eBasicTypeUnsignedChar; + case SimpleTypeKind::NarrowCharacter: + return lldb::eBasicTypeChar; + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return lldb::eBasicTypeSignedChar; + case SimpleTypeKind::Character16: + return lldb::eBasicTypeChar16; + case SimpleTypeKind::Character32: + return lldb::eBasicTypeChar32; + case SimpleTypeKind::Complex80: + return lldb::eBasicTypeLongDoubleComplex; + case SimpleTypeKind::Complex64: + return lldb::eBasicTypeDoubleComplex; + case SimpleTypeKind::Complex32: + return lldb::eBasicTypeFloatComplex; + case SimpleTypeKind::Float128: + case SimpleTypeKind::Float80: + return lldb::eBasicTypeLongDouble; + case SimpleTypeKind::Float64: + return lldb::eBasicTypeDouble; + case SimpleTypeKind::Float32: + return lldb::eBasicTypeFloat; + case SimpleTypeKind::Float16: + return lldb::eBasicTypeHalf; + case SimpleTypeKind::Int128: + return lldb::eBasicTypeInt128; + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return lldb::eBasicTypeLongLong; + case SimpleTypeKind::Int32: + return lldb::eBasicTypeInt; + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + return lldb::eBasicTypeShort; + case SimpleTypeKind::UInt128: + return lldb::eBasicTypeUnsignedInt128; + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + return lldb::eBasicTypeUnsignedLongLong; + case SimpleTypeKind::HResult: + case SimpleTypeKind::UInt32: + return lldb::eBasicTypeUnsignedInt; + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + return lldb::eBasicTypeUnsignedShort; + case SimpleTypeKind::Int32Long: + return lldb::eBasicTypeLong; + case SimpleTypeKind::UInt32Long: + return lldb::eBasicTypeUnsignedLong; + case SimpleTypeKind::Void: + return lldb::eBasicTypeVoid; + case SimpleTypeKind::WideCharacter: + return lldb::eBasicTypeWChar; + default: + return lldb::eBasicTypeInvalid; + } +} + +static size_t GetTypeSizeForSimpleKind(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Int128: + case SimpleTypeKind::UInt128: + case SimpleTypeKind::Float128: + return 16; + case SimpleTypeKind::Complex80: + case SimpleTypeKind::Float80: + return 10; + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Complex64: + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + case SimpleTypeKind::Float64: + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return 8; + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Character32: + case SimpleTypeKind::Complex32: + case SimpleTypeKind::Float32: + case SimpleTypeKind::Int32: + case SimpleTypeKind::Int32Long: + case SimpleTypeKind::UInt32Long: + case SimpleTypeKind::HResult: + case SimpleTypeKind::UInt32: + return 4; + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Character16: + case SimpleTypeKind::Float16: + case SimpleTypeKind::Int16: + case SimpleTypeKind::Int16Short: + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + case SimpleTypeKind::WideCharacter: + return 2; + case SimpleTypeKind::Boolean8: + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + case SimpleTypeKind::NarrowCharacter: + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return 1; + case SimpleTypeKind::Void: + default: + return 0; + } +} + +static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) { + switch (kind) { + case SimpleTypeKind::Boolean128: + case SimpleTypeKind::Boolean16: + case SimpleTypeKind::Boolean32: + case SimpleTypeKind::Boolean64: + case SimpleTypeKind::Boolean8: + return "bool"; + case SimpleTypeKind::Byte: + case SimpleTypeKind::UnsignedCharacter: + return "unsigned char"; + case SimpleTypeKind::NarrowCharacter: + return "char"; + case SimpleTypeKind::SignedCharacter: + case SimpleTypeKind::SByte: + return "signed chr"; + case SimpleTypeKind::Character16: + return "char16_t"; + case SimpleTypeKind::Character32: + return "char32_t"; + case SimpleTypeKind::Complex80: + case SimpleTypeKind::Complex64: + case SimpleTypeKind::Complex32: + return "complex"; + case SimpleTypeKind::Float128: + case SimpleTypeKind::Float80: + return "long double"; + case SimpleTypeKind::Float64: + return "double"; + case SimpleTypeKind::Float32: + return "float"; + case SimpleTypeKind::Float16: + return "single"; + case SimpleTypeKind::Int128: + return "__int128"; + case SimpleTypeKind::Int64: + case SimpleTypeKind::Int64Quad: + return "__int64"; + case SimpleTypeKind::Int32: + return "int"; + case SimpleTypeKind::Int16: + return "short"; + case SimpleTypeKind::UInt128: + return "unsigned __int128"; + case SimpleTypeKind::UInt64: + case SimpleTypeKind::UInt64Quad: + return "unsigned __int64"; + case SimpleTypeKind::HResult: + return "HRESULT"; + case SimpleTypeKind::UInt32: + return "unsigned"; + case SimpleTypeKind::UInt16: + case SimpleTypeKind::UInt16Short: + return "unsigned short"; + case SimpleTypeKind::Int32Long: + return "long"; + case SimpleTypeKind::UInt32Long: + return "unsigned long"; + case SimpleTypeKind::Void: + return "void"; + case SimpleTypeKind::WideCharacter: + return "wchar_t"; + default: + return ""; + } +} + +static bool IsClassRecord(TypeLeafKind kind) { + switch (kind) { + case LF_STRUCTURE: + case LF_CLASS: + case LF_INTERFACE: + return true; + default: + return false; + } +} + +static PDB_SymType GetPdbSymType(TpiStream &tpi, TypeIndex ti) { + if (ti.isSimple()) { + if (ti.getSimpleMode() == SimpleTypeMode::Direct) + return PDB_SymType::BuiltinType; + return PDB_SymType::PointerType; + } + + CVType cvt = tpi.getType(ti); + TypeLeafKind kind = cvt.kind(); + if (kind != LF_MODIFIER) + return CVTypeToPDBType(kind); + + // If this is an LF_MODIFIER, look through it to get the kind that it + // modifies. Note that it's not possible to have an LF_MODIFIER that + // modifies another LF_MODIFIER, although this would handle that anyway. + ModifierRecord mr; + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, mr)); + return GetPdbSymType(tpi, mr.ModifiedType); +} + +static clang::TagTypeKind TranslateUdtKind(const TagRecord &cr) { + switch (cr.Kind) { + case TypeRecordKind::Class: + return clang::TTK_Class; + case TypeRecordKind::Struct: + return clang::TTK_Struct; + case TypeRecordKind::Union: + return clang::TTK_Union; + case TypeRecordKind::Interface: + return clang::TTK_Interface; + case TypeRecordKind::Enum: + return clang::TTK_Enum; + default: + lldbassert(false && "Invalid tag record kind!"); + return clang::TTK_Struct; + } +} + void SymbolFileNativePDB::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, @@ -216,6 +488,11 @@ void SymbolFileNativePDB::InitializeObject() { m_obj_load_address = m_obj_file->GetFileOffset(); m_index->SetLoadAddress(m_obj_load_address); m_index->ParseSectionContribs(); + + TypeSystem *ts = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus); + m_clang = llvm::dyn_cast_or_null(ts); + m_importer = llvm::make_unique(); + lldbassert(m_clang); } uint32_t SymbolFileNativePDB::GetNumCompileUnits() { @@ -295,6 +572,329 @@ SymbolFileNativePDB::CreateCompileUnit(const CompilandIndexItem &cci) { return cu_sp; } +lldb::TypeSP SymbolFileNativePDB::CreateModifierType(PdbSymUid type_uid, + const ModifierRecord &mr) { + TpiStream &stream = m_index->tpi(); + + TypeSP t = GetOrCreateType(mr.ModifiedType); + CompilerType ct = t->GetForwardCompilerType(); + if ((mr.Modifiers & ModifierOptions::Const) != ModifierOptions::None) + ct = ct.AddConstModifier(); + if ((mr.Modifiers & ModifierOptions::Volatile) != ModifierOptions::None) + ct = ct.AddVolatileModifier(); + std::string name; + if (mr.ModifiedType.isSimple()) + name = GetSimpleTypeName(mr.ModifiedType.getSimpleKind()); + else + name = computeTypeName(stream.typeCollection(), mr.ModifiedType); + Declaration decl; + return std::make_shared(type_uid.toOpaqueId(), m_clang->GetSymbolFile(), + ConstString(name), t->GetByteSize(), nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + ct, Type::eResolveStateFull); +} + +lldb::TypeSP SymbolFileNativePDB::CreatePointerType( + PdbSymUid type_uid, const llvm::codeview::PointerRecord &pr) { + TypeSP pointee = GetOrCreateType(pr.ReferentType); + CompilerType pointee_ct = pointee->GetForwardCompilerType(); + lldbassert(pointee_ct); + Declaration decl; + + if (pr.isPointerToMember()) { + MemberPointerInfo mpi = pr.getMemberInfo(); + TypeSP class_type = GetOrCreateType(mpi.ContainingType); + + CompilerType ct = ClangASTContext::CreateMemberPointerType( + class_type->GetLayoutCompilerType(), pointee_ct); + + return std::make_shared( + type_uid.toOpaqueId(), m_clang->GetSymbolFile(), ConstString(), + pr.getSize(), nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, + Type::eResolveStateFull); + } + + CompilerType pointer_ct = pointee_ct; + if (pr.getMode() == PointerMode::LValueReference) + pointer_ct = pointer_ct.GetLValueReferenceType(); + else if (pr.getMode() == PointerMode::RValueReference) + pointer_ct = pointer_ct.GetRValueReferenceType(); + else + pointer_ct = pointer_ct.GetPointerType(); + + if ((pr.getOptions() & PointerOptions::Const) != PointerOptions::None) + pointer_ct = pointer_ct.AddConstModifier(); + + if ((pr.getOptions() & PointerOptions::Volatile) != PointerOptions::None) + pointer_ct = pointer_ct.AddVolatileModifier(); + + if ((pr.getOptions() & PointerOptions::Restrict) != PointerOptions::None) + pointer_ct = pointer_ct.AddRestrictModifier(); + + return std::make_shared(type_uid.toOpaqueId(), m_clang->GetSymbolFile(), + ConstString(), pr.getSize(), nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + pointer_ct, Type::eResolveStateFull); +} + +lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti) { + if (ti.getSimpleMode() != SimpleTypeMode::Direct) { + PdbSymUid uid = + PdbSymUid::makeTypeSymId(PDB_SymType::PointerType, ti, false); + TypeSP direct_sp = GetOrCreateType(ti.makeDirect()); + CompilerType ct = direct_sp->GetFullCompilerType(); + ct = ct.GetPointerType(); + uint32_t pointer_size = 4; + switch (ti.getSimpleMode()) { + case SimpleTypeMode::FarPointer32: + case SimpleTypeMode::NearPointer32: + pointer_size = 4; + break; + case SimpleTypeMode::NearPointer64: + pointer_size = 8; + break; + default: + // 128-bit and 16-bit pointers unsupported. + return nullptr; + } + Declaration decl; + return std::make_shared(uid.toOpaqueId(), m_clang->GetSymbolFile(), + ConstString(), pointer_size, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + ct, Type::eResolveStateFull); + } + + PdbSymUid uid = PdbSymUid::makeTypeSymId(PDB_SymType::BuiltinType, ti, false); + if (ti.getSimpleKind() == SimpleTypeKind::NotTranslated) + return nullptr; + + lldb::BasicType bt = GetCompilerTypeForSimpleKind(ti.getSimpleKind()); + lldbassert(bt != lldb::eBasicTypeInvalid); + CompilerType ct = m_clang->GetBasicType(bt); + size_t size = GetTypeSizeForSimpleKind(ti.getSimpleKind()); + + llvm::StringRef type_name = GetSimpleTypeName(ti.getSimpleKind()); + + Declaration decl; + return std::make_shared(uid.toOpaqueId(), m_clang->GetSymbolFile(), + ConstString(type_name), size, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + ct, Type::eResolveStateFull); +} + +lldb::TypeSP SymbolFileNativePDB::CreateClassStructUnion( + PdbSymUid type_uid, llvm::StringRef name, size_t size, + clang::TagTypeKind ttk, clang::MSInheritanceAttr::Spelling inheritance) { + + // Some UDT with trival ctor has zero length. Just ignore. + if (size == 0) + return nullptr; + + // Ignore unnamed-tag UDTs. + name = DropNameScope(name); + if (name.empty()) + return nullptr; + + clang::DeclContext *decl_context = m_clang->GetTranslationUnitDecl(); + + lldb::AccessType access = + (ttk == clang::TTK_Class) ? lldb::eAccessPrivate : lldb::eAccessPublic; + + ClangASTMetadata metadata; + metadata.SetUserID(type_uid.toOpaqueId()); + metadata.SetIsDynamicCXXType(false); + + CompilerType ct = + m_clang->CreateRecordType(decl_context, access, name.str().c_str(), ttk, + lldb::eLanguageTypeC_plus_plus, &metadata); + lldbassert(ct.IsValid()); + + clang::CXXRecordDecl *record_decl = + m_clang->GetAsCXXRecordDecl(ct.GetOpaqueQualType()); + lldbassert(record_decl); + + clang::MSInheritanceAttr *attr = clang::MSInheritanceAttr::CreateImplicit( + *m_clang->getASTContext(), inheritance); + record_decl->addAttr(attr); + + ClangASTContext::StartTagDeclarationDefinition(ct); + + // Even if it's possible, don't complete it at this point. Just mark it + // forward resolved, and if/when LLDB needs the full definition, it can + // ask us. + ClangASTContext::SetHasExternalStorage(ct.GetOpaqueQualType(), true); + + // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE. + Declaration decl; + return std::make_shared(type_uid.toOpaqueId(), m_clang->GetSymbolFile(), + ConstString(name), size, nullptr, + LLDB_INVALID_UID, Type::eEncodingIsUID, decl, + ct, Type::eResolveStateForward); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbSymUid type_uid, + const ClassRecord &cr) { + clang::TagTypeKind ttk = TranslateUdtKind(cr); + + clang::MSInheritanceAttr::Spelling inheritance = + GetMSInheritance(m_index->tpi().typeCollection(), cr); + return CreateClassStructUnion(type_uid, cr.getName(), cr.getSize(), ttk, + inheritance); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbSymUid type_uid, + const UnionRecord &ur) { + return CreateClassStructUnion( + type_uid, ur.getName(), ur.getSize(), clang::TTK_Union, + clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance); +} + +lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbSymUid type_uid, + const EnumRecord &er) { + llvm::StringRef name = DropNameScope(er.getName()); + + clang::DeclContext *decl_context = m_clang->GetTranslationUnitDecl(); + + Declaration decl; + TypeSP underlying_type = GetOrCreateType(er.UnderlyingType); + CompilerType enum_ct = m_clang->CreateEnumerationType( + name.str().c_str(), decl_context, decl, + underlying_type->GetFullCompilerType(), er.isScoped()); + + ClangASTContext::StartTagDeclarationDefinition(enum_ct); + + // We're just going to forward resolve this for now. We'll complete + // it only if the user requests. + return std::make_shared( + type_uid.toOpaqueId(), m_clang->GetSymbolFile(), ConstString(name), + underlying_type->GetByteSize(), nullptr, LLDB_INVALID_UID, + lldb_private::Type::eEncodingIsUID, decl, enum_ct, + lldb_private::Type::eResolveStateForward); +} + +TypeSP SymbolFileNativePDB::CreateType(PdbSymUid type_uid) { + const PdbTypeSymId &tsid = type_uid.asTypeSym(); + TypeIndex index(tsid.index); + + if (index.getIndex() < TypeIndex::FirstNonSimpleIndex) + return CreateSimpleType(index); + + TpiStream &stream = tsid.is_ipi ? m_index->ipi() : m_index->tpi(); + CVType cvt = stream.getType(index); + + if (cvt.kind() == LF_MODIFIER) { + ModifierRecord modifier; + llvm::cantFail( + TypeDeserializer::deserializeAs(cvt, modifier)); + return CreateModifierType(type_uid, modifier); + } + + if (cvt.kind() == LF_POINTER) { + PointerRecord pointer; + llvm::cantFail( + TypeDeserializer::deserializeAs(cvt, pointer)); + return CreatePointerType(type_uid, pointer); + } + + if (IsClassRecord(cvt.kind())) { + ClassRecord cr; + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, cr)); + return CreateTagType(type_uid, cr); + } + + if (cvt.kind() == LF_ENUM) { + EnumRecord er; + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, er)); + return CreateTagType(type_uid, er); + } + + if (cvt.kind() == LF_UNION) { + UnionRecord ur; + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, ur)); + return CreateTagType(type_uid, ur); + } + + return nullptr; +} + +TypeSP SymbolFileNativePDB::CreateAndCacheType(PdbSymUid type_uid) { + // If they search for a UDT which is a forward ref, try and resolve the full + // decl and just map the forward ref uid to the full decl record. + llvm::Optional full_decl_uid; + if (type_uid.tag() == PDB_SymType::UDT || + type_uid.tag() == PDB_SymType::Enum) { + const PdbTypeSymId &type_id = type_uid.asTypeSym(); + TypeIndex ti(type_id.index); + lldbassert(!ti.isSimple()); + CVType cvt = m_index->tpi().getType(ti); + + if (IsForwardRefUdt(cvt)) { + auto expected_full_ti = m_index->tpi().findFullDeclForForwardRef(ti); + if (!expected_full_ti) + llvm::consumeError(expected_full_ti.takeError()); + else { + full_decl_uid = PdbSymUid::makeTypeSymId( + type_uid.tag(), *expected_full_ti, type_id.is_ipi); + + // It's possible that a lookup would occur for the full decl causing it + // to be cached, then a second lookup would occur for the forward decl. + // We don't want to create a second full decl, so make sure the full + // decl hasn't already been cached. + auto full_iter = m_types.find(full_decl_uid->toOpaqueId()); + if (full_iter != m_types.end()) { + TypeSP result = full_iter->second; + // Map the forward decl to the TypeSP for the full decl so we can take + // the fast path next time. + m_types[type_uid.toOpaqueId()] = result; + return result; + } + } + } + } + + PdbSymUid best_uid = full_decl_uid ? *full_decl_uid : type_uid; + TypeSP result = CreateType(best_uid); + m_types[best_uid.toOpaqueId()] = result; + // If we had both a forward decl and a full decl, make both point to the new + // type. + if (full_decl_uid) + m_types[type_uid.toOpaqueId()] = result; + + const PdbTypeSymId &type_id = best_uid.asTypeSym(); + if (best_uid.tag() == PDB_SymType::UDT || + best_uid.tag() == PDB_SymType::Enum) { + clang::TagDecl *record_decl = + m_clang->GetAsTagDecl(result->GetForwardCompilerType()); + lldbassert(record_decl); + + TypeIndex ti(type_id.index); + CVType cvt = m_index->tpi().getType(ti); + m_uid_to_decl[best_uid.toOpaqueId()] = record_decl; + m_decl_to_status[record_decl] = + DeclStatus(best_uid.toOpaqueId(), Type::eResolveStateForward); + } + return result; +} + +TypeSP SymbolFileNativePDB::GetOrCreateType(PdbSymUid type_uid) { + lldbassert(PdbSymUid::isTypeSym(type_uid.tag())); + // We can't use try_emplace / overwrite here because the process of creating + // a type could create nested types, which could invalidate iterators. So + // we have to do a 2-phase lookup / insert. + auto iter = m_types.find(type_uid.toOpaqueId()); + if (iter != m_types.end()) + return iter->second; + + return CreateAndCacheType(type_uid); +} + +lldb::TypeSP +SymbolFileNativePDB::GetOrCreateType(llvm::codeview::TypeIndex ti) { + PDB_SymType pdbst = GetPdbSymType(m_index->tpi(), ti); + PdbSymUid tuid = PdbSymUid::makeTypeSymId(pdbst, ti, false); + return GetOrCreateType(tuid); +} + FunctionSP SymbolFileNativePDB::GetOrCreateFunction(PdbSymUid func_uid, const SymbolContext &sc) { lldbassert(func_uid.tag() == PDB_SymType::Function); @@ -595,7 +1195,18 @@ uint32_t SymbolFileNativePDB::FindTypes( const CompilerDeclContext *parent_decl_ctx, bool append, uint32_t max_matches, llvm::DenseSet &searched_symbol_files, TypeMap &types) { - return 0; + if (!append) + types.Clear(); + if (!name) + return 0; + + searched_symbol_files.clear(); + searched_symbol_files.insert(this); + + // There is an assumption 'name' is not a regex + size_t match_count = FindTypesByName(name.GetStringRef(), max_matches, types); + + return match_count; } size_t @@ -604,13 +1215,96 @@ SymbolFileNativePDB::FindTypes(const std::vector &context, return 0; } +size_t SymbolFileNativePDB::FindTypesByName(llvm::StringRef name, + uint32_t max_matches, + TypeMap &types) { + + size_t match_count = 0; + std::vector matches = m_index->tpi().findRecordsByName(name); + if (max_matches > 0 && max_matches < matches.size()) + matches.resize(max_matches); + + for (TypeIndex ti : matches) { + TypeSP type = GetOrCreateType(ti); + if (!type) + continue; + + types.Insert(type); + ++match_count; + } + return match_count; +} + size_t SymbolFileNativePDB::ParseTypes(const SymbolContext &sc) { return 0; } Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) { - return nullptr; + auto iter = m_types.find(type_uid); + // lldb should not be passing us non-sensical type uids. the only way it + // could have a type uid in the first place is if we handed it out, in which + // case we should know about the type. So this is not a get-or-create type + // operation, it is strictly a get, and the type is guaranteed to exist. + // + // However, since the implementation is not yet complete, we don't currently + // support all possible use cases. For example, we currently create all + // functions with indices of 0 for the signature type simply because this is + // not yet implemented. At the time the function object is created we should + // be creating an lldb::TypeSP for this, adding it to the m_types, and + // returning a valid Type object for it and putting it in this map. Once all + // cases like this are handled, we can promote this to an assert. + if (iter == m_types.end()) + return nullptr; + return &*iter->second; } bool SymbolFileNativePDB::CompleteType(CompilerType &compiler_type) { + // If this is not in our map, it's an error. + clang::TagDecl *tag_decl = m_clang->GetAsTagDecl(compiler_type); + lldbassert(tag_decl); + auto status_iter = m_decl_to_status.find(tag_decl); + lldbassert(status_iter != m_decl_to_status.end()); + + // If it's already complete, just return. + DeclStatus &status = status_iter->second; + if (status.status == Type::eResolveStateFull) + return true; + + PdbSymUid uid = PdbSymUid::fromOpaqueId(status.uid); + lldbassert(uid.tag() == PDB_SymType::UDT || uid.tag() == PDB_SymType::Enum); + + const PdbTypeSymId &type_id = uid.asTypeSym(); + + ClangASTContext::SetHasExternalStorage(compiler_type.GetOpaqueQualType(), + false); + + // In CreateAndCacheType, we already go out of our way to resolve forward + // ref UDTs to full decls, and the uids we vend out always refer to full + // decls if a full decl exists in the debug info. So if we don't have a full + // decl here, it means one doesn't exist in the debug info, and we can't + // complete the type. + CVType cvt = m_index->tpi().getType(TypeIndex(type_id.index)); + if (IsForwardRefUdt(cvt)) + return false; + + auto types_iter = m_types.find(uid.toOpaqueId()); + lldbassert(types_iter != m_types.end()); + + TypeIndex field_list_ti = GetFieldListIndex(cvt); + CVType field_list_cvt = m_index->tpi().getType(field_list_ti); + if (field_list_cvt.kind() != LF_FIELDLIST) + return false; + + // Visit all members of this class, then perform any finalization necessary + // to complete the class. + UdtRecordCompleter completer(uid, compiler_type, *tag_decl, *this); + auto error = + llvm::codeview::visitMemberRecordStream(field_list_cvt.data(), completer); + completer.complete(); + + status.status = Type::eResolveStateFull; + if (!error) + return true; + + llvm::consumeError(std::move(error)); return false; } diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h index 2a5b5471..2b1f893 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -10,6 +10,7 @@ #ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H #define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_SYMBOLFILENATIVEPDB_H +#include "lldb/Symbol/ClangASTImporter.h" #include "lldb/Symbol/SymbolFile.h" #include "llvm/ADT/DenseMap.h" @@ -144,22 +145,63 @@ public: llvm::pdb::PDBFile &GetPDBFile() { return m_index->pdb(); } const llvm::pdb::PDBFile &GetPDBFile() const { return m_index->pdb(); } + ClangASTContext &GetASTContext() { return *m_clang; } + ClangASTImporter &GetASTImporter() { return *m_importer; } + private: + void AddBaseClassesToLayout(CompilerType &derived_ct, + ClangASTImporter::LayoutInfo &layout, + const llvm::codeview::ClassRecord &record); + void AddMembersToLayout(ClangASTImporter::LayoutInfo &layout, + const llvm::codeview::TagRecord &record); + void AddMethodsToLayout(ClangASTImporter::LayoutInfo &layout, + const llvm::codeview::TagRecord &record); + + size_t FindTypesByName(llvm::StringRef name, uint32_t max_matches, + TypeMap &types); + + lldb::TypeSP CreateModifierType(PdbSymUid type_uid, + const llvm::codeview::ModifierRecord &mr); + lldb::TypeSP CreatePointerType(PdbSymUid type_uid, + const llvm::codeview::PointerRecord &pr); + lldb::TypeSP CreateSimpleType(llvm::codeview::TypeIndex ti); + lldb::TypeSP CreateTagType(PdbSymUid type_uid, + const llvm::codeview::ClassRecord &cr); + lldb::TypeSP CreateTagType(PdbSymUid type_uid, + const llvm::codeview::EnumRecord &er); + lldb::TypeSP CreateTagType(PdbSymUid type_uid, + const llvm::codeview::UnionRecord &ur); + lldb::TypeSP + CreateClassStructUnion(PdbSymUid type_uid, llvm::StringRef name, size_t size, + clang::TagTypeKind ttk, + clang::MSInheritanceAttr::Spelling inheritance); + lldb::FunctionSP GetOrCreateFunction(PdbSymUid func_uid, const SymbolContext &sc); lldb::CompUnitSP GetOrCreateCompileUnit(const CompilandIndexItem &cci); + lldb::TypeSP GetOrCreateType(PdbSymUid type_uid); + lldb::TypeSP GetOrCreateType(llvm::codeview::TypeIndex ti); lldb::FunctionSP CreateFunction(PdbSymUid func_uid, const SymbolContext &sc); lldb::CompUnitSP CreateCompileUnit(const CompilandIndexItem &cci); + lldb::TypeSP CreateType(PdbSymUid type_uid); + lldb::TypeSP CreateAndCacheType(PdbSymUid type_uid); llvm::BumpPtrAllocator m_allocator; lldb::addr_t m_obj_load_address = 0; std::unique_ptr m_index; + std::unique_ptr m_importer; + ClangASTContext *m_clang = nullptr; + + llvm::DenseMap m_decl_to_status; + + llvm::DenseMap m_uid_to_decl; llvm::DenseMap m_functions; llvm::DenseMap m_compilands; + llvm::DenseMap m_types; }; } // namespace npdb diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp new file mode 100644 index 0000000..905a3f0 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -0,0 +1,188 @@ +#include "UdtRecordCompleter.h" + +#include "PdbIndex.h" +#include "PdbSymUid.h" +#include "PdbUtil.h" +#include "SymbolFileNativePDB.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Utility/LLDBAssert.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" + +#include "llvm/DebugInfo/CodeView/TypeDeserializer.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/PDBTypes.h" + +using namespace llvm::codeview; +using namespace llvm::pdb; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::npdb; + +using Error = llvm::Error; + +UdtRecordCompleter::UdtRecordCompleter(PdbSymUid uid, CompilerType &derived_ct, + clang::TagDecl &tag_decl, + SymbolFileNativePDB &symbol_file) + : m_uid(uid), m_derived_ct(derived_ct), m_tag_decl(tag_decl), + m_symbol_file(symbol_file) { + TpiStream &tpi = symbol_file.m_index->tpi(); + TypeIndex ti(uid.asTypeSym().index); + CVType cvt = tpi.getType(ti); + switch (cvt.kind()) { + case LF_ENUM: + lldbassert(uid.tag() == PDB_SymType::Enum); + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, m_cvr.er)); + break; + case LF_UNION: + lldbassert(uid.tag() == PDB_SymType::UDT); + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, m_cvr.ur)); + break; + case LF_CLASS: + case LF_STRUCTURE: + lldbassert(uid.tag() == PDB_SymType::UDT); + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, m_cvr.cr)); + break; + default: + llvm_unreachable("unreachable!"); + } +} + +lldb::opaque_compiler_type_t UdtRecordCompleter::AddBaseClassForTypeIndex( + llvm::codeview::TypeIndex ti, llvm::codeview::MemberAccess access) { + TypeSP base_type = m_symbol_file.GetOrCreateType(ti); + CompilerType base_ct = base_type->GetFullCompilerType(); + + CVType udt_cvt = m_symbol_file.m_index->tpi().getType(ti); + + lldb::opaque_compiler_type_t base_qt = base_ct.GetOpaqueQualType(); + clang::CXXBaseSpecifier *base_spec = + m_symbol_file.GetASTContext().CreateBaseClassSpecifier( + base_qt, TranslateMemberAccess(access), false, + udt_cvt.kind() == LF_CLASS); + + m_bases.push_back(base_spec); + return base_qt; +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + BaseClassRecord &base) { + lldb::opaque_compiler_type_t base_qt = + AddBaseClassForTypeIndex(base.Type, base.getAccess()); + + auto decl = m_symbol_file.GetASTContext().GetAsCXXRecordDecl(base_qt); + lldbassert(decl); + + auto offset = clang::CharUnits::fromQuantity(base.getBaseOffset()); + m_layout.base_offsets.insert(std::make_pair(decl, offset)); + + return llvm::Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + VirtualBaseClassRecord &base) { + AddBaseClassForTypeIndex(base.BaseType, base.getAccess()); + + // FIXME: Handle virtual base offsets. + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + ListContinuationRecord &cont) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + VFPtrRecord &vfptr) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember( + CVMemberRecord &cvr, StaticDataMemberRecord &static_data_member) { + TypeSP member_type = m_symbol_file.GetOrCreateType(static_data_member.Type); + CompilerType complete_member_type = member_type->GetFullCompilerType(); + + lldb::AccessType access = + TranslateMemberAccess(static_data_member.getAccess()); + ClangASTContext::AddVariableToRecordType( + m_derived_ct, static_data_member.Name.str().c_str(), complete_member_type, + access); + + // FIXME: Add a PdbSymUid namespace for field list members and update + // the m_uid_to_decl map with this decl. + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + NestedTypeRecord &nested) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + DataMemberRecord &data_member) { + + TypeSP member_type = m_symbol_file.GetOrCreateType(data_member.Type); + CompilerType complete_member_type = member_type->GetFullCompilerType(); + + lldb::AccessType access = TranslateMemberAccess(data_member.getAccess()); + + clang::FieldDecl *decl = ClangASTContext::AddFieldToRecordType( + m_derived_ct, data_member.Name.str().c_str(), complete_member_type, + access, 0); + // FIXME: Add a PdbSymUid namespace for field list members and update + // the m_uid_to_decl map with this decl. + + uint64_t offset = data_member.FieldOffset * 8; + m_layout.field_offsets.insert(std::make_pair(decl, offset)); + + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + OneMethodRecord &one_method) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + OverloadedMethodRecord &overloaded) { + return Error::success(); +} + +Error UdtRecordCompleter::visitKnownMember(CVMemberRecord &cvr, + EnumeratorRecord &enumerator) { + ClangASTContext &clang = m_symbol_file.GetASTContext(); + + Declaration decl; + llvm::StringRef name = DropNameScope(enumerator.getName()); + lldbassert(m_uid.tag() == PDB_SymType::Enum); + TypeSP underlying_type = + m_symbol_file.GetOrCreateType(m_cvr.er.getUnderlyingType()); + + lldb::opaque_compiler_type_t enum_qt = m_derived_ct.GetOpaqueQualType(); + + CompilerType enumerator_type = clang.GetEnumerationIntegerType(enum_qt); + uint64_t byte_size = underlying_type->GetByteSize(); + clang.AddEnumerationValueToEnumerationType( + m_derived_ct.GetOpaqueQualType(), enumerator_type, decl, + name.str().c_str(), enumerator.Value.getSExtValue(), + byte_size * 8); + return Error::success(); +} + +void UdtRecordCompleter::complete() { + ClangASTContext &clang = m_symbol_file.GetASTContext(); + clang.SetBaseClassesForClassType(m_derived_ct.GetOpaqueQualType(), + m_bases.data(), m_bases.size()); + ClangASTContext::DeleteBaseClassSpecifiers(m_bases.data(), m_bases.size()); + + clang.AddMethodOverridesForCXXRecordType(m_derived_ct.GetOpaqueQualType()); + ClangASTContext::BuildIndirectFields(m_derived_ct); + ClangASTContext::CompleteTagDeclarationDefinition(m_derived_ct); + + if (auto *record_decl = llvm::dyn_cast(&m_tag_decl)) { + m_symbol_file.GetASTImporter().InsertRecordDecl(record_decl, m_layout); + } +} \ No newline at end of file diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h new file mode 100644 index 0000000..76c1ad5 --- /dev/null +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h @@ -0,0 +1,68 @@ +//===-- SymbolFileNativePDB.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H +#define LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H + +#include "lldb/Symbol/ClangASTImporter.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" + +#include "PdbSymUid.h" + +namespace clang { +class CXXBaseSpecifier; +class TagDecl; +} // namespace clang + +namespace lldb_private { +class Type; +class CompilerType; +namespace npdb { +class SymbolFileNativePDB; + +class UdtRecordCompleter : public llvm::codeview::TypeVisitorCallbacks { + union UdtTagRecord { + UdtTagRecord() {} + llvm::codeview::UnionRecord ur; + llvm::codeview::ClassRecord cr; + llvm::codeview::EnumRecord er; + } m_cvr; + + PdbSymUid m_uid; + CompilerType &m_derived_ct; + clang::TagDecl &m_tag_decl; + SymbolFileNativePDB &m_symbol_file; + std::vector m_bases; + ClangASTImporter::LayoutInfo m_layout; + +public: + UdtRecordCompleter(PdbSymUid uid, CompilerType &derived_ct, + clang::TagDecl &tag_decl, + SymbolFileNativePDB &symbol_file); + +#define MEMBER_RECORD(EnumName, EnumVal, Name) \ + llvm::Error visitKnownMember(llvm::codeview::CVMemberRecord &CVR, \ + llvm::codeview::Name##Record &Record) override; +#define MEMBER_RECORD_ALIAS(EnumName, EnumVal, Name, AliasName) +#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" + + void complete(); + +private: + lldb::opaque_compiler_type_t + AddBaseClassForTypeIndex(llvm::codeview::TypeIndex ti, + llvm::codeview::MemberAccess access); +}; + +} // namespace npdb +} // namespace lldb_private + +#endif // LLDB_PLUGINS_SYMBOLFILE_NATIVEPDB_UDTRECORDCOMPLETER_H -- 2.7.4