Index: lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-functions.lldbinit =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-functions.lldbinit +++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-functions.lldbinit @@ -0,0 +1,8 @@ + +break set -n main +break set -n static_fn +break set -n varargs_fn + +target modules dump ast + +quit 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 @@ -1,20 +0,0 @@ -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/Inputs/ast-types.lldbinit =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-types.lldbinit +++ lldb/trunk/lit/SymbolFile/NativePDB/Inputs/ast-types.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/ast-functions-msvc.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/ast-functions-msvc.cpp +++ lldb/trunk/lit/SymbolFile/NativePDB/ast-functions-msvc.cpp @@ -0,0 +1,7 @@ +// clang-format off +// REQUIRES: msvc + +// RUN: %build --compiler=msvc --nodefaultlib -o %t.exe -- %S/ast-functions.cpp + +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \ +// RUN: %p/Inputs/ast-functions.lldbinit 2>&1 | FileCheck %S/ast-functions.cpp Index: lldb/trunk/lit/SymbolFile/NativePDB/ast-functions.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/ast-functions.cpp +++ lldb/trunk/lit/SymbolFile/NativePDB/ast-functions.cpp @@ -0,0 +1,29 @@ +// clang-format off +// REQUIRES: lld + +// RUN: %build --compiler=clang-cl --nodefaultlib -o %t.exe -- %s + +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \ +// RUN: %p/Inputs/ast-functions.lldbinit 2>&1 | FileCheck %s + +static int static_fn() { + return 42; +} + +int varargs_fn(int x, int y, ...) { + return x + y; +} + +int main(int argc, char **argv) { + return static_fn() + varargs_fn(argc, argc); +} + +// CHECK: TranslationUnitDecl +// CHECK-NEXT: |-FunctionDecl {{.*}} main 'int (int, char **)' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} argc 'int' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} argv 'char **' +// CHECK-NEXT: |-FunctionDecl {{.*}} static_fn 'int ()' static +// CHECK-NEXT: |-FunctionDecl {{.*}} varargs_fn 'int (int, int, ...)' +// CHECK-NEXT: | |-ParmVarDecl {{.*}} x 'int' +// CHECK-NEXT: | `-ParmVarDecl {{.*}} y 'int' +// CHECK-NEXT: `- 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 @@ -1,131 +0,0 @@ -// clang-format off -// REQUIRES: lld - -// Test various interesting cases for AST reconstruction. -// RUN: %build --compiler=clang-cl --nodefaultlib -o %t.exe -- %s -// 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 - struct C { - T ABCMember; - }; - - // Let's try a template specialization with a different implementation - template<> - struct C { - void *ABCSpecializationMember; - }; - } - - // Let's make sure we can distinguish classes and namespaces. Also let's try - // a non-type template parameter. - template - struct C { - class D { - int ACDMember = 0; - C *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 - 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 ABCInt; -A::B::C ABCFloat; -A::B::C 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 AnonInt; -Anonymous> AnonABCVoid; -Anonymous>::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 = TE_A -// CHECK: (A::B::C) ABCInt = (ABCMember = 0) -// CHECK: (A::B::C) ABCFloat = (ABCMember = 0) -// CHECK: (A::B::C) ABCVoid = (ABCSpecializationMember = 0x{{0+}}) -// CHECK: (A::C<0>) AC0 = {} -// CHECK: (A::C<-1>) ACNeg1 = {} -// CHECK: (A::C<0>::D) AC0D = (ACDMember = 0, CPtr = 0x{{0+}}) -// CHECK: (A::C<-1>::D) ACNeg1D = (ACDMember = 0, CPtr = 0x{{0+}}) -// CHECK: (A::D) AD = {} -// CHECK: (A::D::E) ADE = (ADDMember = 0) -// 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: | `-EnumConstantDecl {{.*}} TE_A 'TrivialE' -// CHECK: |-NamespaceDecl {{.*}} A -// CHECK: | |-NamespaceDecl {{.*}} B -// CHECK: | | |-CXXRecordDecl {{.*}} struct C definition -// CHECK: | | | `-FieldDecl {{.*}} ABCMember 'int' -// CHECK: | | |-CXXRecordDecl {{.*}} struct C definition -// CHECK: | | | `-FieldDecl {{.*}} ABCMember 'float' -// CHECK: | | `-CXXRecordDecl {{.*}} struct C 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; -} Index: lldb/trunk/lit/SymbolFile/NativePDB/ast-types.cpp =================================================================== --- lldb/trunk/lit/SymbolFile/NativePDB/ast-types.cpp +++ lldb/trunk/lit/SymbolFile/NativePDB/ast-types.cpp @@ -0,0 +1,131 @@ +// clang-format off +// REQUIRES: lld + +// Test various interesting cases for AST reconstruction. +// RUN: %build --compiler=clang-cl --nodefaultlib -o %t.exe -- %s +// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \ +// RUN: %p/Inputs/ast-types.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 + struct C { + T ABCMember; + }; + + // Let's try a template specialization with a different implementation + template<> + struct C { + void *ABCSpecializationMember; + }; + } + + // Let's make sure we can distinguish classes and namespaces. Also let's try + // a non-type template parameter. + template + struct C { + class D { + int ACDMember = 0; + C *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 + 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 ABCInt; +A::B::C ABCFloat; +A::B::C 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 AnonInt; +Anonymous> AnonABCVoid; +Anonymous>::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 = TE_A +// CHECK: (A::B::C) ABCInt = (ABCMember = 0) +// CHECK: (A::B::C) ABCFloat = (ABCMember = 0) +// CHECK: (A::B::C) ABCVoid = (ABCSpecializationMember = 0x{{0+}}) +// CHECK: (A::C<0>) AC0 = {} +// CHECK: (A::C<-1>) ACNeg1 = {} +// CHECK: (A::C<0>::D) AC0D = (ACDMember = 0, CPtr = 0x{{0+}}) +// CHECK: (A::C<-1>::D) ACNeg1D = (ACDMember = 0, CPtr = 0x{{0+}}) +// CHECK: (A::D) AD = {} +// CHECK: (A::D::E) ADE = (ADDMember = 0) +// 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: | `-EnumConstantDecl {{.*}} TE_A 'TrivialE' +// CHECK: |-NamespaceDecl {{.*}} A +// CHECK: | |-NamespaceDecl {{.*}} B +// CHECK: | | |-CXXRecordDecl {{.*}} struct C definition +// CHECK: | | | `-FieldDecl {{.*}} ABCMember 'int' +// CHECK: | | |-CXXRecordDecl {{.*}} struct C definition +// CHECK: | | | `-FieldDecl {{.*}} ABCMember 'float' +// CHECK: | | `-CXXRecordDecl {{.*}} struct C 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; +} 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 @@ -112,6 +112,8 @@ size_t ParseVariablesForContext(const SymbolContext &sc) override { return 0; } + + CompilerDeclContext GetDeclContextContainingUID(lldb::user_id_t uid) override; Type *ResolveTypeUID(lldb::user_id_t type_uid) override; llvm::Optional GetDynamicArrayInfoForUID( lldb::user_id_t type_uid, @@ -217,7 +219,7 @@ llvm::DenseMap m_decl_to_status; - llvm::DenseMap m_uid_to_decl; + llvm::DenseMap m_uid_to_decl; llvm::DenseMap m_parent_types; 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 @@ -37,6 +37,7 @@ #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/RecordName.h" #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" +#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" @@ -538,16 +539,114 @@ if (!func_range.GetBaseAddress().IsValid()) return nullptr; - Type *func_type = nullptr; + ProcSym proc(static_cast(sym_record.kind())); + cantFail(SymbolDeserializer::deserializeAs(sym_record, proc)); + TypeSP func_type = GetOrCreateType(proc.FunctionType); - // FIXME: Resolve types and mangled names. - PdbTypeSymId sig_id(TypeIndex::None(), false); - Mangled mangled(getSymbolName(sym_record)); + PdbTypeSymId sig_id(proc.FunctionType, false); + Mangled mangled(proc.Name); FunctionSP func_sp = std::make_shared( sc.comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled, - func_type, func_range); + func_type.get(), func_range); sc.comp_unit->AddFunction(func_sp); + + clang::StorageClass storage = clang::SC_None; + if (sym_record.kind() == S_LPROC32) + storage = clang::SC_Static; + + // There are two ways we could retrieve the parameter list. The first is by + // iterating the arguments on the function signature type, however that would + // only tell us the types of the arguments and not the names. The second is + // to iterate the CVSymbol records that follow the S_GPROC32 / S_LPROC32 until + // we have the correct number of arguments as stated by the function + // signature. The latter has more potential to go wrong in the face of + // improper debug info simply because we're assuming more about the layout of + // the records, but it is the only way to get argument names. + CVType sig_cvt; + CVType arg_list_cvt; + ProcedureRecord sig_record; + ArgListRecord arg_list_record; + + sig_cvt = m_index->tpi().getType(proc.FunctionType); + if (sig_cvt.kind() != LF_PROCEDURE) + return func_sp; + cantFail( + TypeDeserializer::deserializeAs(sig_cvt, sig_record)); + + CompilerDeclContext context = + GetDeclContextContainingUID(toOpaqueUid(func_id)); + + clang::DeclContext *decl_context = + static_cast(context.GetOpaqueDeclContext()); + clang::FunctionDecl *function_decl = m_clang->CreateFunctionDeclaration( + decl_context, proc.Name.str().c_str(), + func_type->GetForwardCompilerType(), storage, false); + + lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0); + m_uid_to_decl[toOpaqueUid(func_id)] = function_decl; + CVSymbolArray scope = limitSymbolArrayToScope( + cci->m_debug_stream.getSymbolArray(), func_id.offset); + + uint32_t params_remaining = sig_record.getParameterCount(); + auto begin = scope.begin(); + auto end = scope.end(); + std::vector params; + while (begin != end && params_remaining > 0) { + uint32_t record_offset = begin.offset(); + CVSymbol sym = *begin++; + + TypeIndex param_type; + llvm::StringRef param_name; + switch (sym.kind()) { + case S_REGREL32: { + RegRelativeSym reg(SymbolRecordKind::RegRelativeSym); + cantFail(SymbolDeserializer::deserializeAs(sym, reg)); + param_type = reg.Type; + param_name = reg.Name; + break; + } + case S_REGISTER: { + RegisterSym reg(SymbolRecordKind::RegisterSym); + cantFail(SymbolDeserializer::deserializeAs(sym, reg)); + param_type = reg.Index; + param_name = reg.Name; + break; + } + case S_LOCAL: { + LocalSym local(SymbolRecordKind::LocalSym); + cantFail(SymbolDeserializer::deserializeAs(sym, local)); + if ((local.Flags & LocalSymFlags::IsParameter) == LocalSymFlags::None) + continue; + param_type = local.Type; + param_name = local.Name; + break; + } + case S_BLOCK32: + // All parameters should come before the first block. If that isn't the + // case, then perhaps this is bad debug info that doesn't contain + // information about all parameters. + params_remaining = 0; + continue; + default: + continue; + } + + PdbCompilandSymId param_uid(func_id.modi, record_offset); + TypeSP type_sp = GetOrCreateType(param_type); + clang::ParmVarDecl *param = m_clang->CreateParameterDeclaration( + param_name.str().c_str(), type_sp->GetForwardCompilerType(), + clang::SC_None); + lldbassert(m_uid_to_decl.count(toOpaqueUid(param_uid)) == 0); + + m_uid_to_decl[toOpaqueUid(param_uid)] = param; + params.push_back(param); + --params_remaining; + } + + if (!params.empty()) + m_clang->SetFunctionParameters(function_decl, params.data(), params.size()); + return func_sp; } @@ -714,6 +813,8 @@ std::pair SymbolFileNativePDB::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) { + // FIXME: Move this to GetDeclContextContainingUID. + llvm::ms_demangle::Demangler demangler; StringView sv(record.UniqueName.begin(), record.UniqueName.size()); llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); @@ -1520,6 +1621,32 @@ size_t SymbolFileNativePDB::ParseTypes(const SymbolContext &sc) { return 0; } +CompilerDeclContext +SymbolFileNativePDB::GetDeclContextContainingUID(lldb::user_id_t uid) { + // FIXME: This should look up the uid, decide if it's a symbol or a type, and + // depending which it is, find the appropriate DeclContext. Possibilities: + // For classes and typedefs: + // * Function + // * Namespace + // * Global + // * Block + // * Class + // For field list members: + // * Class + // For variables: + // * Function + // * Namespace + // * Global + // * Block + // For functions: + // * Namespace + // * Global + // * Class + // + // It is an error to call this function with a uid for any other symbol type. + return {m_clang, m_clang->GetTranslationUnitDecl()}; +} + Type *SymbolFileNativePDB::ResolveTypeUID(lldb::user_id_t type_uid) { auto iter = m_types.find(type_uid); // lldb should not be passing us non-sensical type uids. the only way it Index: llvm/trunk/include/llvm/DebugInfo/CodeView/SymbolRecordHelpers.h =================================================================== --- llvm/trunk/include/llvm/DebugInfo/CodeView/SymbolRecordHelpers.h +++ llvm/trunk/include/llvm/DebugInfo/CodeView/SymbolRecordHelpers.h @@ -50,7 +50,11 @@ /// Given a symbol P for which symbolOpensScope(P) == true, return the /// corresponding end offset. -uint32_t getScopeEndOffset(const CVSymbol &symbol); +uint32_t getScopeEndOffset(const CVSymbol &Symbol); + +CVSymbolArray limitSymbolArrayToScope(const CVSymbolArray &Symbols, + uint32_t ScopeBegin); + } // namespace codeview } // namespace llvm Index: llvm/trunk/include/llvm/Support/BinaryStreamArray.h =================================================================== --- llvm/trunk/include/llvm/Support/BinaryStreamArray.h +++ llvm/trunk/include/llvm/Support/BinaryStreamArray.h @@ -113,6 +113,15 @@ bool empty() const { return Stream.getLength() == 0; } + VarStreamArray substream(uint32_t Begin, + uint32_t End) const { + assert(Begin >= Skew); + // We should never cut off the beginning of the stream since it might be + // skewed, meaning the initial bytes are important. + BinaryStreamRef NewStream = Stream.slice(0, End); + return {NewStream, E, Begin}; + } + /// given an offset into the array's underlying stream, return an /// iterator to the record at that offset. This is considered unsafe /// since the behavior is undefined if \p Offset does not refer to the Index: llvm/trunk/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp =================================================================== --- llvm/trunk/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp +++ llvm/trunk/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp @@ -50,4 +50,15 @@ assert(false && "Unknown record type"); return 0; } -} \ No newline at end of file +} + +CVSymbolArray +llvm::codeview::limitSymbolArrayToScope(const CVSymbolArray &Symbols, + uint32_t ScopeBegin) { + CVSymbol Opener = *Symbols.at(ScopeBegin); + assert(symbolOpensScope(Opener.kind())); + uint32_t EndOffset = getScopeEndOffset(Opener); + CVSymbol Closer = *Symbols.at(EndOffset); + EndOffset += Closer.RecordData.size(); + return Symbols.substream(ScopeBegin, EndOffset); +}