This revision was automatically updated to reflect the committed changes.
Closed by commit rL346429: [NativePDB] Higher fidelity reconstruction of AST
from Debug Info. (authored by zturner, committed by ).
Herald added a subscriber: llvm-commits.
Changed prior to commit:
https://reviews.llvm.org/D54216?vs=172989&id=173198#toc
Repository:
rL LLVM
https://reviews.llvm.org/D54216
Files:
lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-reconstruction.lldbinit
lldb/trunk/lit/SymbolFile/NativePDB/Inputs/function-types-classes.lldbinit
lldb/trunk/lit/SymbolFile/NativePDB/Inputs/globals-classes.lldbinit
lldb/trunk/lit/SymbolFile/NativePDB/ast-reconstruction.cpp
lldb/trunk/lit/SymbolFile/NativePDB/function-types-classes.cpp
lldb/trunk/lit/SymbolFile/NativePDB/global-classes.cpp
lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
llvm/trunk/include/llvm/DebugInfo/CodeView/TypeRecord.h
Index: llvm/trunk/include/llvm/DebugInfo/CodeView/TypeRecord.h
===================================================================
--- llvm/trunk/include/llvm/DebugInfo/CodeView/TypeRecord.h
+++ llvm/trunk/include/llvm/DebugInfo/CodeView/TypeRecord.h
@@ -429,6 +429,10 @@
return (Options & ClassOptions::ForwardReference) != ClassOptions::None;
}
+ bool containsNestedClass() const {
+ return (Options & ClassOptions::ContainsNestedClass) != ClassOptions::None;
+ }
+
bool isScoped() const {
return (Options & ClassOptions::Scoped) != ClassOptions::None;
}
Index: lldb/trunk/lit/SymbolFile/NativePDB/ast-reconstruction.cpp
===================================================================
--- lldb/trunk/lit/SymbolFile/NativePDB/ast-reconstruction.cpp
+++ lldb/trunk/lit/SymbolFile/NativePDB/ast-reconstruction.cpp
@@ -0,0 +1,131 @@
+// clang-format off
+// REQUIRES: lld
+
+// Test various interesting cases for AST reconstruction.
+// 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/ast-reconstruction.lldbinit 2>&1 | FileCheck %s
+
+// Test trivial versions of each tag type.
+class TrivialC {};
+struct TrivialS {};
+union TrivialU {};
+enum TrivialE {TE_A};
+
+// Test reconstruction of DeclContext hierarchies.
+namespace A {
+ namespace B {
+ template<typename T>
+ struct C {
+ T ABCMember;
+ };
+
+ // Let's try a template specialization with a different implementation
+ template<>
+ struct C<void> {
+ void *ABCSpecializationMember;
+ };
+ }
+
+ // Let's make sure we can distinguish classes and namespaces. Also let's try
+ // a non-type template parameter.
+ template<int N>
+ struct C {
+ class D {
+ int ACDMember = 0;
+ C<N - 1> *CPtr = nullptr;
+ };
+ };
+
+ struct D {
+ // Let's make a nested class with the same name as another nested class
+ // elsewhere, and confirm that they appear in the right DeclContexts in
+ // the AST.
+ struct E {
+ int ADDMember;
+ };
+ };
+}
+
+
+// Let's try an anonymous namespace.
+namespace {
+ template<typename T>
+ struct Anonymous {
+ int AnonymousMember;
+ // And a nested class within an anonymous namespace
+ struct D {
+ int AnonymousDMember;
+ };
+ };
+}
+
+TrivialC TC;
+TrivialS TS;
+TrivialU TU;
+TrivialE TE;
+
+A::B::C<int> ABCInt;
+A::B::C<float> ABCFloat;
+A::B::C<void> ABCVoid;
+
+A::C<0> AC0;
+A::C<-1> ACNeg1;
+
+A::C<0>::D AC0D;
+A::C<-1>::D ACNeg1D;
+A::D AD;
+A::D::E ADE;
+
+// FIXME: Anonymous namespaces aren't working correctly.
+Anonymous<int> AnonInt;
+Anonymous<A::B::C<void>> AnonABCVoid;
+Anonymous<A::B::C<int>>::D AnonABCVoidD;
+
+// FIXME: Enum size isn't being correctly determined.
+// FIXME: Can't read memory for variable values.
+
+// CHECK: (TrivialC) TC = {}
+// CHECK: (TrivialS) TS = {}
+// CHECK: (TrivialU) TU = {}
+// CHECK: (TrivialE) TE = <Unable to determine byte size.>
+// CHECK: (A::B::C<int>) ABCInt = (ABCMember = <read memory from {{.*}} failed>)
+// CHECK: (A::B::C<float>) ABCFloat = (ABCMember = <read memory from {{.*}} failed>)
+// CHECK: (A::B::C<void>) ABCVoid = (ABCSpecializationMember = <read memory from {{.*}} failed>)
+// CHECK: (A::C<0>) AC0 = {}
+// CHECK: (A::C<-1>) ACNeg1 = {}
+// CHECK: (A::C<0>::D) AC0D = (ACDMember = <read memory from {{.*}} failed>, CPtr = <read memory from {{.*}} failed>)
+// CHECK: (A::C<-1>::D) ACNeg1D = (ACDMember = <read memory from {{.*}} failed>, CPtr = <read memory from {{.*}} failed>)
+// CHECK: (A::D) AD = {}
+// CHECK: (A::D::E) ADE = (ADDMember = <read memory from {{.*}} failed>)
+// CHECK: Dumping clang ast for 1 modules.
+// CHECK: TranslationUnitDecl {{.*}}
+// CHECK: |-CXXRecordDecl {{.*}} class TrivialC definition
+// CHECK: |-CXXRecordDecl {{.*}} struct TrivialS definition
+// CHECK: |-CXXRecordDecl {{.*}} union TrivialU definition
+// CHECK: |-EnumDecl {{.*}} TrivialE
+// CHECK: |-NamespaceDecl {{.*}} A
+// CHECK: | |-NamespaceDecl {{.*}} B
+// CHECK: | | |-CXXRecordDecl {{.*}} struct C<int> definition
+// CHECK: | | | `-FieldDecl {{.*}} ABCMember 'int'
+// CHECK: | | |-CXXRecordDecl {{.*}} struct C<float> definition
+// CHECK: | | | `-FieldDecl {{.*}} ABCMember 'float'
+// CHECK: | | `-CXXRecordDecl {{.*}} struct C<void> definition
+// CHECK: | | `-FieldDecl {{.*}} ABCSpecializationMember 'void *'
+// CHECK: | |-CXXRecordDecl {{.*}} struct C<0> definition
+// CHECK: | | `-CXXRecordDecl {{.*}} class D definition
+// CHECK: | | |-FieldDecl {{.*}} ACDMember 'int'
+// CHECK: | | `-FieldDecl {{.*}} CPtr 'A::C<-1> *'
+// CHECK: | |-CXXRecordDecl {{.*}} struct C<-1> definition
+// CHECK: | | `-CXXRecordDecl {{.*}} class D definition
+// CHECK: | | |-FieldDecl {{.*}} ACDMember 'int'
+// CHECK: | | `-FieldDecl {{.*}} CPtr 'A::C<-2> *'
+// CHECK: | |-CXXRecordDecl {{.*}} struct C<-2>
+// CHECK: | `-CXXRecordDecl {{.*}} struct D definition
+// CHECK: | `-CXXRecordDecl {{.*}} struct E definition
+// CHECK: | `-FieldDecl {{.*}} ADDMember 'int'
+
+int main(int argc, char **argv) {
+ return 0;
+}
\ No newline at end of file
Index: lldb/trunk/lit/SymbolFile/NativePDB/function-types-classes.cpp
===================================================================
--- lldb/trunk/lit/SymbolFile/NativePDB/function-types-classes.cpp
+++ lldb/trunk/lit/SymbolFile/NativePDB/function-types-classes.cpp
@@ -2,7 +2,7 @@
// REQUIRES: lld
// Test that we can display function signatures with class types.
-// RUN: clang-cl /Z7 /GS- /GR- /c -Xclang -fkeep-static-consts /Fo%t.obj -- %s
+// RUN: clang-cl /Z7 /GS- /GR- /c -fstandalone-debug -Xclang -fkeep-static-consts /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/function-types-classes.lldbinit | FileCheck %s
@@ -66,6 +66,13 @@
};
};
+// clang (incorrectly) doesn't emit debug information for outer classes
+// unless they are instantiated. They should also be emitted if there
+// is an inner class which is instantiated.
+A::C ForceInstantiateAC;
+B ForceInstantiateB;
+B::A ForceInstantiateBA;
+
template<typename T>
struct TC {};
@@ -81,38 +88,45 @@
// classes nested in namespaces and inner classes
-// FIXME: LLDB with native pdb plugin doesn't currently resolve nested names
-// correctly, because it requires creating clang::NamespaceDecl or
-// clang::RecordDecl for the outer namespace or classes. PDB doesn't contain
-// sufficient information to distinguish namespace scopes from nested class
-// scopes, so the best we can hope for is a heuristic reconstruction of the
-// clang AST based on demangling the type's unique name. However, this is
-// as-yet unimplemented in the native PDB plugin, so for now all of these will
-// all just look like `S` when LLDB prints them.
auto e = &three<A::B::S*, B::A::S*, A::C::S&>;
-// CHECK: (S *(*)(S *, S &)) e = {{.*}}
+// CHECK: (A::B::S *(*)(B::A::S *, A::C::S &)) e = {{.*}}
auto f = &three<A::C::S&, A::B::S*, B::A::S*>;
-// CHECK: (S &(*)(S *, S *)) f = {{.*}}
+// CHECK: (A::C::S &(*)(A::B::S *, B::A::S *)) f = {{.*}}
auto g = &three<B::A::S*, A::C::S&, A::B::S*>;
-// CHECK: (S *(*)(S &, S *)) g = {{.*}}
+// CHECK: (B::A::S *(*)(A::C::S &, A::B::S *)) g = {{.*}}
// parameter types that are themselves template instantiations.
auto h = &four<TC<void>, TC<int>, TC<TC<int>>, TC<A::B::S>>;
-// Note the awkward space in TC<TC<int> >. This is because this is how template
-// instantiations are emitted by the compiler, as the fully instantiated name.
-// Only via reconstruction of the AST through the mangled type name (see above
-// comment) can we hope to do better than this).
-// CHECK: (TC<void> (*)(TC<int>, TC<TC<int> >, S>)) h = {{.*}}
+// CHECK: (TC<void> (*)(TC<int>, TC<struct TC<int>>, TC<struct A::B::S>)) h = {{.*}}
auto i = &nullary<A::B::S>;
-// CHECK: (S (*)()) i = {{.*}}
+// CHECK: (A::B::S (*)()) i = {{.*}}
// Make sure we can handle types that don't have complete debug info.
struct Incomplete;
auto incomplete = &three<Incomplete*, Incomplete**, const Incomplete*>;
// CHECK: (Incomplete *(*)(Incomplete **, const Incomplete *)) incomplete = {{.*}}
+// CHECK: TranslationUnitDecl {{.*}}
+// CHECK: |-CXXRecordDecl {{.*}} class C definition
+// CHECK: |-CXXRecordDecl {{.*}} union U definition
+// CHECK: |-EnumDecl {{.*}} E
+// CHECK: |-CXXRecordDecl {{.*}} struct S definition
+// CHECK: |-CXXRecordDecl {{.*}} struct B
+// CHECK: | |-CXXRecordDecl {{.*}} struct A
+// CHECK: | | |-CXXRecordDecl {{.*}} struct S
+// CHECK: |-NamespaceDecl {{.*}} A
+// CHECK: | |-CXXRecordDecl {{.*}} struct C
+// CHECK: | | |-CXXRecordDecl {{.*}} struct S
+// CHECK: | `-NamespaceDecl {{.*}} B
+// CHECK: | `-CXXRecordDecl {{.*}} struct S definition
+// CHECK: |-CXXRecordDecl {{.*}} struct TC<int> definition
+// CHECK: |-CXXRecordDecl {{.*}} struct TC<struct TC<int>> definition
+// CHECK: |-CXXRecordDecl {{.*}} struct TC<struct A::B::S> definition
+// CHECK: |-CXXRecordDecl {{.*}} struct TC<void> definition
+// CHECK: |-CXXRecordDecl {{.*}} struct Incomplete
+
int main(int argc, char **argv) {
return 0;
}
Index: lldb/trunk/lit/SymbolFile/NativePDB/Inputs/function-types-classes.lldbinit
===================================================================
--- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/function-types-classes.lldbinit
+++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/function-types-classes.lldbinit
@@ -9,4 +9,6 @@
target variable i
target variable incomplete
+target modules dump ast
+
quit
Index: lldb/trunk/lit/SymbolFile/NativePDB/Inputs/globals-classes.lldbinit
===================================================================
--- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/globals-classes.lldbinit
+++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/globals-classes.lldbinit
@@ -11,4 +11,6 @@
target variable -T PointersInstance
target variable -T ReferencesInstance
+target modules dump ast
+
quit
\ No newline at end of file
Index: lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-reconstruction.lldbinit
===================================================================
--- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-reconstruction.lldbinit
+++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-reconstruction.lldbinit
@@ -0,0 +1,20 @@
+target variable TC
+target variable TS
+target variable TU
+target variable TE
+
+target variable ABCInt
+target variable ABCFloat
+target variable ABCVoid
+
+target variable AC0
+target variable ACNeg1
+
+target variable AC0D
+target variable ACNeg1D
+target variable AD
+target variable ADE
+
+target modules dump ast
+
+quit
Index: lldb/trunk/lit/SymbolFile/NativePDB/global-classes.cpp
===================================================================
--- lldb/trunk/lit/SymbolFile/NativePDB/global-classes.cpp
+++ lldb/trunk/lit/SymbolFile/NativePDB/global-classes.cpp
@@ -270,6 +270,94 @@
// CHECK-NEXT: (long long &) m = {{.*}}
// CHECK-NEXT: }
+// CHECK: Dumping clang ast for 1 modules.
+// CHECK: TranslationUnitDecl {{.*}}
+// CHECK: |-CXXRecordDecl {{.*}} class ClassWithPadding definition
+// CHECK: | |-FieldDecl {{.*}} a 'char'
+// CHECK: | |-FieldDecl {{.*}} b 'short'
+// CHECK: | |-FieldDecl {{.*}} c 'char [2]'
+// CHECK: | |-FieldDecl {{.*}} d 'int'
+// CHECK: | |-FieldDecl {{.*}} e 'char'
+// CHECK: | |-FieldDecl {{.*}} f 'int'
+// CHECK: | |-FieldDecl {{.*}} g 'long long'
+// CHECK: | |-FieldDecl {{.*}} h 'char [3]'
+// CHECK: | |-FieldDecl {{.*}} i 'long long'
+// CHECK: | |-FieldDecl {{.*}} j 'char [2]'
+// CHECK: | |-FieldDecl {{.*}} k 'long long'
+// CHECK: | |-FieldDecl {{.*}} l 'char'
+// CHECK: | `-FieldDecl {{.*}} m 'long long'
+// CHECK: |-CXXRecordDecl {{.*}} class ClassNoPadding definition
+// CHECK: | |-FieldDecl {{.*}} a 'unsigned char'
+// CHECK: | |-FieldDecl {{.*}} b 'char'
+// CHECK: | |-FieldDecl {{.*}} c 'bool'
+// CHECK: | |-FieldDecl {{.*}} d 'bool'
+// CHECK: | |-FieldDecl {{.*}} e 'short'
+// CHECK: | |-FieldDecl {{.*}} f 'unsigned short'
+// CHECK: | |-FieldDecl {{.*}} g 'unsigned int'
+// CHECK: | |-FieldDecl {{.*}} h 'int'
+// CHECK: | |-FieldDecl {{.*}} i 'unsigned long'
+// CHECK: | |-FieldDecl {{.*}} j 'long'
+// CHECK: | |-FieldDecl {{.*}} k 'float'
+// CHECK: | |-FieldDecl {{.*}} l 'EnumType'
+// CHECK: | |-FieldDecl {{.*}} m 'double'
+// CHECK: | |-FieldDecl {{.*}} n 'unsigned long long'
+// CHECK: | |-FieldDecl {{.*}} o 'long long'
+// CHECK: | `-FieldDecl {{.*}} p 'int [5]'
+// CHECK: |-EnumDecl {{.*}} EnumType
+// CHECK: | |-EnumConstantDecl {{.*}} A 'unsigned int'
+// CHECK: | `-EnumConstantDecl {{.*}} B 'unsigned int'
+// CHECK: |-CXXRecordDecl {{.*}} struct DerivedClass definition
+// CHECK: | |-public 'BaseClass<int>'
+// CHECK: | `-FieldDecl {{.*}} DerivedMember 'int'
+// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<int> definition
+// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
+// CHECK: |-CXXRecordDecl {{.*}} struct EBO definition
+// CHECK: | |-public 'EmptyBase'
+// CHECK: | `-FieldDecl {{.*}} Member 'int'
+// CHECK: |-CXXRecordDecl {{.*}} struct EmptyBase definition
+// CHECK: |-CXXRecordDecl {{.*}} struct PaddedBases definition
+// CHECK: | |-public 'BaseClass<char>'
+// CHECK: | |-public 'BaseClass<short>'
+// CHECK: | |-public 'BaseClass<int>'
+// CHECK: | `-FieldDecl {{.*}} DerivedMember 'long long'
+// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<char> definition
+// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
+// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<short> definition
+// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
+// CHECK: |-CXXRecordDecl {{.*}} struct <unnamed-type-UnnamedClassInstance> definition
+// CHECK: | |-FieldDecl {{.*}} x 'int'
+// CHECK: | `-FieldDecl {{.*}} EBOC 'EBO'
+// CHECK: |-CXXRecordDecl {{.*}} struct Pointers definition
+// CHECK: | |-FieldDecl {{.*}} a 'void *'
+// CHECK: | |-FieldDecl {{.*}} b 'char *'
+// CHECK: | |-FieldDecl {{.*}} c 'bool *'
+// CHECK: | |-FieldDecl {{.*}} e 'short *'
+// CHECK: | |-FieldDecl {{.*}} f 'unsigned short *'
+// CHECK: | |-FieldDecl {{.*}} g 'unsigned int *'
+// CHECK: | |-FieldDecl {{.*}} h 'int *'
+// CHECK: | |-FieldDecl {{.*}} i 'unsigned long *'
+// CHECK: | |-FieldDecl {{.*}} j 'long *'
+// CHECK: | |-FieldDecl {{.*}} k 'float *'
+// CHECK: | |-FieldDecl {{.*}} l 'EnumType *'
+// CHECK: | |-FieldDecl {{.*}} m 'double *'
+// CHECK: | |-FieldDecl {{.*}} n 'unsigned long long *'
+// CHECK: | `-FieldDecl {{.*}} o 'long long *'
+// CHECK: |-CXXRecordDecl {{.*}} struct References definition
+// CHECK: | |-FieldDecl {{.*}} a 'char &'
+// CHECK: | |-FieldDecl {{.*}} b 'bool &'
+// CHECK: | |-FieldDecl {{.*}} c 'short &'
+// CHECK: | |-FieldDecl {{.*}} d 'unsigned short &'
+// CHECK: | |-FieldDecl {{.*}} e 'unsigned int &'
+// CHECK: | |-FieldDecl {{.*}} f 'int &'
+// CHECK: | |-FieldDecl {{.*}} g 'unsigned long &'
+// CHECK: | |-FieldDecl {{.*}} h 'long &'
+// CHECK: | |-FieldDecl {{.*}} i 'float &'
+// CHECK: | |-FieldDecl {{.*}} j 'EnumType &'
+// CHECK: | |-FieldDecl {{.*}} k 'double &'
+// CHECK: | |-FieldDecl {{.*}} l 'unsigned long long &'
+// CHECK: | `-FieldDecl {{.*}} m 'long long &'
+// CHECK: `-<undeserialized declarations>
+
int main(int argc, char **argv) {
return 0;
}
\ No newline at end of file
Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
===================================================================
--- lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
+++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
@@ -13,14 +13,57 @@
#include "lldb/lldb-enumerations.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
#include <tuple>
#include <utility>
namespace lldb_private {
namespace npdb {
+struct CVTagRecord {
+ enum Kind { Class, Struct, Union, Enum };
+
+ static CVTagRecord create(llvm::codeview::CVType type);
+
+ Kind kind() const { return m_kind; }
+
+ const llvm::codeview::TagRecord &asTag() const {
+ if (m_kind == Struct || m_kind == Class)
+ return cvclass;
+ if (m_kind == Enum)
+ return cvenum;
+ return cvunion;
+ }
+
+ const llvm::codeview::ClassRecord &asClass() const {
+ assert(m_kind == Struct || m_kind == Class);
+ return cvclass;
+ }
+
+ const llvm::codeview::EnumRecord &asEnum() const {
+ assert(m_kind == Enum);
+ return cvenum;
+ }
+
+ const llvm::codeview::UnionRecord &asUnion() const {
+ assert(m_kind == Union);
+ return cvunion;
+ }
+
+private:
+ CVTagRecord(llvm::codeview::ClassRecord &&c);
+ CVTagRecord(llvm::codeview::UnionRecord &&u);
+ CVTagRecord(llvm::codeview::EnumRecord &&e);
+ Kind m_kind;
+ union {
+ llvm::codeview::ClassRecord cvclass;
+ llvm::codeview::EnumRecord cvenum;
+ llvm::codeview::UnionRecord cvunion;
+ };
+};
+
struct SegmentOffset {
SegmentOffset() = default;
SegmentOffset(uint16_t s, uint32_t o) : segment(s), offset(o) {}
@@ -56,6 +99,7 @@
}
bool IsForwardRefUdt(llvm::codeview::CVType cvt);
+bool IsTagRecord(llvm::codeview::CVType cvt);
lldb::AccessType TranslateMemberAccess(llvm::codeview::MemberAccess access);
llvm::codeview::TypeIndex GetFieldListIndex(llvm::codeview::CVType cvt);
Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
===================================================================
--- lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -162,6 +162,11 @@
void DumpClangAST(Stream &s) override;
private:
+ std::pair<clang::DeclContext *, std::string>
+ CreateDeclInfoForType(const llvm::codeview::TagRecord &record,
+ llvm::codeview::TypeIndex ti);
+
+ void PreprocessTpiStream();
size_t FindTypesByName(llvm::StringRef name, uint32_t max_matches,
TypeMap &types);
@@ -180,10 +185,9 @@
const llvm::codeview::ArrayRecord &ar);
lldb::TypeSP CreateProcedureType(PdbSymUid type_uid,
const llvm::codeview::ProcedureRecord &pr);
- lldb::TypeSP
- CreateClassStructUnion(PdbSymUid type_uid, llvm::StringRef name, size_t size,
- clang::TagTypeKind ttk,
- clang::MSInheritanceAttr::Spelling inheritance);
+ lldb::TypeSP CreateClassStructUnion(
+ PdbSymUid type_uid, const llvm::codeview::TagRecord &record, size_t size,
+ clang::TagTypeKind ttk, clang::MSInheritanceAttr::Spelling inheritance);
lldb::FunctionSP GetOrCreateFunction(PdbSymUid func_uid,
const SymbolContext &sc);
@@ -209,6 +213,8 @@
llvm::DenseMap<clang::TagDecl *, DeclStatus> m_decl_to_status;
llvm::DenseMap<lldb::user_id_t, clang::TagDecl *> m_uid_to_decl;
+ llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex>
+ m_parent_types;
llvm::DenseMap<lldb::user_id_t, lldb::VariableSP> m_global_vars;
llvm::DenseMap<lldb::user_id_t, lldb::FunctionSP> m_functions;
Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
===================================================================
--- lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
+++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -21,6 +21,38 @@
using namespace llvm::codeview;
using namespace llvm::pdb;
+CVTagRecord CVTagRecord::create(CVType type) {
+ assert(IsTagRecord(type) && "type is not a tag record!");
+ switch (type.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE: {
+ ClassRecord cr;
+ llvm::cantFail(TypeDeserializer::deserializeAs<ClassRecord>(type, cr));
+ return CVTagRecord(std::move(cr));
+ }
+ case LF_UNION: {
+ UnionRecord ur;
+ llvm::cantFail(TypeDeserializer::deserializeAs<UnionRecord>(type, ur));
+ return CVTagRecord(std::move(ur));
+ }
+ case LF_ENUM: {
+ EnumRecord er;
+ llvm::cantFail(TypeDeserializer::deserializeAs<EnumRecord>(type, er));
+ return CVTagRecord(std::move(er));
+ }
+ default:
+ llvm_unreachable("Unreachable!");
+ }
+}
+
+CVTagRecord::CVTagRecord(ClassRecord &&c)
+ : cvclass(std::move(c)),
+ m_kind(cvclass.Kind == TypeRecordKind::Struct ? Struct : Class) {}
+CVTagRecord::CVTagRecord(UnionRecord &&u)
+ : cvunion(std::move(u)), m_kind(Union) {}
+CVTagRecord::CVTagRecord(EnumRecord &&e) : cvenum(std::move(e)), m_kind(Enum) {}
+
PDB_SymType lldb_private::npdb::CVSymToPDBSym(SymbolKind kind) {
switch (kind) {
case S_COMPILE3:
@@ -94,6 +126,8 @@
return PDB_SymType::Enum;
case LF_PROCEDURE:
return PDB_SymType::FunctionSig;
+ case LF_BITFIELD:
+ return PDB_SymType::BuiltinType;
default:
lldbassert(false && "Invalid type record kind!");
}
@@ -306,6 +340,18 @@
}
}
+bool lldb_private::npdb::IsTagRecord(llvm::codeview::CVType cvt) {
+ switch (cvt.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_UNION:
+ case LF_ENUM:
+ return true;
+ default:
+ return false;
+ }
+}
+
lldb::AccessType
lldb_private::npdb::TranslateMemberAccess(MemberAccess access) {
switch (access) {
Index: lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
===================================================================
--- lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ lldb/trunk/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -13,13 +13,16 @@
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamBuffer.h"
+#include "lldb/Core/StreamFile.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/ClangASTImporter.h"
#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
+#include "lldb/Symbol/ClangUtil.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Symbol/ObjectFile.h"
@@ -43,14 +46,14 @@
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/PDBTypes.h"
+#include "llvm/Demangle/MicrosoftDemangle.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/BinaryStreamReader.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
-#include "Plugins/Language/CPlusPlus/CPlusPlusNameParser.h"
-
#include "PdbSymUid.h"
#include "PdbUtil.h"
#include "UdtRecordCompleter.h"
@@ -527,9 +530,60 @@
TypeSystem *ts = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus);
m_clang = llvm::dyn_cast_or_null<ClangASTContext>(ts);
m_importer = llvm::make_unique<ClangASTImporter>();
+
+ PreprocessTpiStream();
lldbassert(m_clang);
}
+void SymbolFileNativePDB::PreprocessTpiStream() {
+ LazyRandomTypeCollection &types = m_index->tpi().typeCollection();
+
+ for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) {
+ CVType type = types.getType(*ti);
+ if (!IsTagRecord(type))
+ continue;
+
+ CVTagRecord tag = CVTagRecord::create(type);
+ // We're looking for LF_NESTTYPE records in the field list, so ignore
+ // forward references (no field list), and anything without a nested class
+ // (since there won't be any LF_NESTTYPE records).
+ if (tag.asTag().isForwardRef() || !tag.asTag().containsNestedClass())
+ continue;
+
+ struct ProcessTpiStream : public TypeVisitorCallbacks {
+ ProcessTpiStream(PdbIndex &index, TypeIndex parent,
+ llvm::DenseMap<TypeIndex, TypeIndex> &parents)
+ : index(index), parents(parents), parent(parent) {}
+
+ PdbIndex &index;
+ llvm::DenseMap<TypeIndex, TypeIndex> &parents;
+ TypeIndex parent;
+
+ llvm::Error visitKnownMember(CVMemberRecord &CVR,
+ NestedTypeRecord &Record) override {
+ parents[Record.Type] = parent;
+ CVType child = index.tpi().getType(Record.Type);
+ if (!IsForwardRefUdt(child))
+ return llvm::ErrorSuccess();
+ llvm::Expected<TypeIndex> full_decl =
+ index.tpi().findFullDeclForForwardRef(Record.Type);
+ if (!full_decl) {
+ llvm::consumeError(full_decl.takeError());
+ return llvm::ErrorSuccess();
+ }
+ parents[*full_decl] = parent;
+ return llvm::ErrorSuccess();
+ }
+ };
+
+ CVType field_list = m_index->tpi().getType(tag.asTag().FieldList);
+ ProcessTpiStream process(*m_index, *ti, m_parent_types);
+ llvm::Error error = visitMemberRecordStream(field_list.data(), process);
+ if (error)
+ llvm::consumeError(std::move(error));
+ }
+}
+
uint32_t SymbolFileNativePDB::GetNumCompileUnits() {
const DbiModuleList &modules = m_index->dbi().modules();
uint32_t count = modules.getModuleCount();
@@ -730,16 +784,69 @@
ct, Type::eResolveStateFull);
}
+static std::string RenderDemanglerNode(llvm::ms_demangle::Node *n) {
+ OutputStream OS;
+ initializeOutputStream(nullptr, nullptr, OS, 1024);
+ n->output(OS, llvm::ms_demangle::OF_Default);
+ OS << '\0';
+ return {OS.getBuffer()};
+}
+
+std::pair<clang::DeclContext *, std::string>
+SymbolFileNativePDB::CreateDeclInfoForType(const TagRecord &record,
+ TypeIndex ti) {
+ llvm::ms_demangle::Demangler demangler;
+ StringView sv(record.UniqueName.begin(), record.UniqueName.size());
+ llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv);
+ llvm::ms_demangle::IdentifierNode *idn =
+ ttn->QualifiedName->getUnqualifiedIdentifier();
+ std::string uname = RenderDemanglerNode(idn);
+
+ llvm::ms_demangle::NodeArrayNode *name_components =
+ ttn->QualifiedName->Components;
+ llvm::ArrayRef<llvm::ms_demangle::Node *> scopes(name_components->Nodes,
+ name_components->Count - 1);
+
+ clang::DeclContext *context = m_clang->GetTranslationUnitDecl();
+
+ // If this type doesn't have a parent type in the debug info, then the best we
+ // can do is to say that it's either a series of namespaces (if the scope is
+ // non-empty), or the translation unit (if the scope is empty).
+ auto parent_iter = m_parent_types.find(ti);
+ if (parent_iter == m_parent_types.end()) {
+ if (scopes.empty())
+ return {context, uname};
+
+ for (llvm::ms_demangle::Node *scope : scopes) {
+ auto *nii = static_cast<llvm::ms_demangle::NamedIdentifierNode *>(scope);
+ std::string str = RenderDemanglerNode(nii);
+ context = m_clang->GetUniqueNamespaceDeclaration(str.c_str(), context);
+ }
+ return {context, uname};
+ }
+
+ // Otherwise, all we need to do is get the parent type of this type and
+ // recurse into our lazy type creation / AST reconstruction logic to get an
+ // LLDB TypeSP for the parent. This will cause the AST to automatically get
+ // the right DeclContext created for any parent.
+ TypeSP parent = GetOrCreateType(parent_iter->second);
+ if (!parent)
+ return {context, uname};
+ CompilerType parent_ct = parent->GetForwardCompilerType();
+ clang::QualType qt = ClangUtil::GetCanonicalQualType(parent_ct);
+ context = clang::TagDecl::castToDeclContext(qt->getAsTagDecl());
+ return {context, uname};
+}
+
lldb::TypeSP SymbolFileNativePDB::CreateClassStructUnion(
- PdbSymUid type_uid, llvm::StringRef name, size_t size,
+ PdbSymUid type_uid, const llvm::codeview::TagRecord &record, size_t size,
clang::TagTypeKind ttk, clang::MSInheritanceAttr::Spelling inheritance) {
- // Ignore unnamed-tag UDTs.
- name = DropNameScope(name);
- if (name.empty())
- return nullptr;
-
- clang::DeclContext *decl_context = m_clang->GetTranslationUnitDecl();
+ const PdbTypeSymId &tid = type_uid.asTypeSym();
+ TypeIndex ti(tid.index);
+ clang::DeclContext *decl_context = nullptr;
+ std::string uname;
+ std::tie(decl_context, uname) = CreateDeclInfoForType(record, ti);
lldb::AccessType access =
(ttk == clang::TTK_Class) ? lldb::eAccessPrivate : lldb::eAccessPublic;
@@ -749,8 +856,9 @@
metadata.SetIsDynamicCXXType(false);
CompilerType ct =
- m_clang->CreateRecordType(decl_context, access, name.str().c_str(), ttk,
+ m_clang->CreateRecordType(decl_context, access, uname.c_str(), ttk,
lldb::eLanguageTypeC_plus_plus, &metadata);
+
lldbassert(ct.IsValid());
clang::CXXRecordDecl *record_decl =
@@ -771,7 +879,7 @@
// FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE.
Declaration decl;
return std::make_shared<Type>(type_uid.toOpaqueId(), m_clang->GetSymbolFile(),
- ConstString(name), size, nullptr,
+ ConstString(uname), size, nullptr,
LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
ct, Type::eResolveStateForward);
}
@@ -782,14 +890,13 @@
clang::MSInheritanceAttr::Spelling inheritance =
GetMSInheritance(m_index->tpi().typeCollection(), cr);
- return CreateClassStructUnion(type_uid, cr.getName(), cr.getSize(), ttk,
- inheritance);
+ return CreateClassStructUnion(type_uid, cr, 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,
+ type_uid, ur, ur.getSize(), clang::TTK_Union,
clang::MSInheritanceAttr::Spelling::Keyword_single_inheritance);
}
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits