Index: lldb/lit/SymbolFile/NativePDB/Inputs/ast-functions.lldbinit =================================================================== --- /dev/null +++ lldb/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/lit/SymbolFile/NativePDB/ast-functions-msvc.cpp =================================================================== --- /dev/null +++ lldb/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/lit/SymbolFile/NativePDB/ast-functions.cpp =================================================================== --- /dev/null +++ lldb/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/lit/SymbolFile/NativePDB/ast-types.cpp =================================================================== --- lldb/lit/SymbolFile/NativePDB/ast-types.cpp +++ lldb/lit/SymbolFile/NativePDB/ast-types.cpp @@ -4,7 +4,7 @@ // 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 +// RUN: %p/Inputs/ast-types.lldbinit 2>&1 | FileCheck %s // Test trivial versions of each tag type. class TrivialC {}; Index: lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h =================================================================== --- lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h +++ lldb/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/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ lldb/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/include/llvm/DebugInfo/CodeView/SymbolRecordHelpers.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/SymbolRecordHelpers.h +++ llvm/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/include/llvm/Support/BinaryStreamArray.h =================================================================== --- llvm/include/llvm/Support/BinaryStreamArray.h +++ llvm/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/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp +++ llvm/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); +}