diff options
Diffstat (limited to 'lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp')
| -rw-r--r-- | lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp | 449 |
1 files changed, 414 insertions, 35 deletions
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp index 3f6ce1513f0..8cf332f8a95 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -7,6 +7,9 @@ #include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h" #include "llvm/DebugInfo/CodeView/TypeDeserializer.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Demangle/MicrosoftDemangle.h" @@ -150,8 +153,8 @@ TranslateCallingConvention(llvm::codeview::CallingConvention conv) { } static llvm::Optional<CVTagRecord> -GetNestedTagRecord(const NestedTypeRecord &Record, const CVTagRecord &parent, - TpiStream &tpi) { +GetNestedTagDefinition(const NestedTypeRecord &Record, + const CVTagRecord &parent, TpiStream &tpi) { // An LF_NESTTYPE is essentially a nested typedef / using declaration, but it // is also used to indicate the primary definition of a nested class. That is // to say, if you have: @@ -218,7 +221,7 @@ PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) { llvm::ms_demangle::TagTypeNode *ttn = demangler.parseTagUniqueName(sv); llvm::ms_demangle::IdentifierNode *idn = ttn->QualifiedName->getUnqualifiedIdentifier(); - std::string uname = idn->toString(); + std::string uname = idn->toString(llvm::ms_demangle::OF_NoTagSpecifier); llvm::ms_demangle::NodeArrayNode *name_components = ttn->QualifiedName->Components; @@ -261,15 +264,55 @@ PdbAstBuilder::CreateDeclInfoForType(const TagRecord &record, TypeIndex ti) { return {context, uname}; } +static bool IsUniqueNameEnumTag(llvm::StringRef unique_name) { + if (unique_name.size() < 4) + return false; + return unique_name[3] == 'W'; +} + +static std::string GetParentUniqueName(llvm::StringRef unique_name) { + if (unique_name.size() < 4) + return unique_name; + size_t start = IsUniqueNameEnumTag(unique_name) ? 5 : 4; + size_t end = unique_name.find('@'); + if (end == llvm::StringRef::npos) + return unique_name; + std::string result = unique_name.str(); + return result.erase(start, end - start + 1); +} + void PdbAstBuilder::BuildParentMap() { LazyRandomTypeCollection &types = m_index.tpi().typeCollection(); + llvm::DenseMap<TypeIndex, TypeIndex> forward_to_full; + llvm::DenseMap<TypeIndex, TypeIndex> full_to_forward; + + struct RecordIndices { + TypeIndex forward; + TypeIndex full; + }; + + llvm::StringMap<RecordIndices> record_indices; + for (auto ti = types.getFirst(); ti; ti = types.getNext(*ti)) { CVType type = types.getType(*ti); if (!IsTagRecord(type)) continue; CVTagRecord tag = CVTagRecord::create(type); + + RecordIndices &indices = record_indices[tag.asTag().getUniqueName()]; + if (tag.asTag().isForwardRef()) + indices.forward = *ti; + else + indices.full = *ti; + + if (indices.full != TypeIndex::None() && + indices.forward != TypeIndex::None()) { + forward_to_full[indices.forward] = indices.full; + full_to_forward[indices.full] = indices.forward; + } + // 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). @@ -285,27 +328,26 @@ void PdbAstBuilder::BuildParentMap() { PdbIndex &index; llvm::DenseMap<TypeIndex, TypeIndex> &parents; + + unsigned unnamed_type_index = 1; TypeIndex parent; const CVTagRecord &parent_cvt; llvm::Error visitKnownMember(CVMemberRecord &CVR, NestedTypeRecord &Record) override { + std::string unnamed_type_name; + if (Record.Name.empty()) { + unnamed_type_name = + llvm::formatv("<unnamed-type-$S{0}>", unnamed_type_index).str(); + Record.Name = unnamed_type_name; + ++unnamed_type_index; + } llvm::Optional<CVTagRecord> tag = - GetNestedTagRecord(Record, parent_cvt, index.tpi()); + GetNestedTagDefinition(Record, parent_cvt, index.tpi()); if (!tag) return llvm::ErrorSuccess(); parents[Record.Type] = parent; - if (!tag->asTag().isForwardRef()) - 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(); } }; @@ -316,11 +358,102 @@ void PdbAstBuilder::BuildParentMap() { if (error) llvm::consumeError(std::move(error)); } + + // Now that we know the forward -> full mapping of all type indices, we can + // re-write all the indices. At the end of this process, we want a mapping + // consisting of fwd -> full and full -> full for all child -> parent indices. + // We can re-write the values in place, but for the keys, we must save them + // off so that we don't modify the map in place while also iterating it. + std::vector<TypeIndex> full_keys; + std::vector<TypeIndex> fwd_keys; + for (auto &entry : m_parent_types) { + TypeIndex key, value; + std::tie(key, value) = entry; + + auto iter = forward_to_full.find(value); + if (iter != forward_to_full.end()) + entry.second = iter->second; + + iter = forward_to_full.find(key); + if (iter != forward_to_full.end()) + fwd_keys.push_back(key); + else + full_keys.push_back(key); + } + for (TypeIndex fwd : fwd_keys) { + TypeIndex full = forward_to_full[fwd]; + m_parent_types[full] = m_parent_types[fwd]; + } + for (TypeIndex full : full_keys) { + TypeIndex fwd = full_to_forward[full]; + m_parent_types[fwd] = m_parent_types[full]; + } + + // Now that +} + +static bool isLocalVariableType(SymbolKind K) { + switch (K) { + case S_REGISTER: + case S_REGREL32: + case S_LOCAL: + return true; + default: + break; + } + return false; +} + +static std::string +RenderScopeList(llvm::ArrayRef<llvm::ms_demangle::Node *> nodes) { + lldbassert(!nodes.empty()); + + std::string result = nodes.front()->toString(); + nodes = nodes.drop_front(); + while (!nodes.empty()) { + result += "::"; + result += nodes.front()->toString(llvm::ms_demangle::OF_NoTagSpecifier); + nodes = nodes.drop_front(); + } + return result; +} + +static llvm::Optional<PublicSym32> FindPublicSym(const SegmentOffset &addr, + SymbolStream &syms, + PublicsStream &publics) { + llvm::FixedStreamArray<ulittle32_t> addr_map = publics.getAddressMap(); + auto iter = std::lower_bound( + addr_map.begin(), addr_map.end(), addr, + [&](const ulittle32_t &x, const SegmentOffset &y) { + CVSymbol s1 = syms.readRecord(x); + lldbassert(s1.kind() == S_PUB32); + PublicSym32 p1; + llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(s1, p1)); + if (p1.Segment < y.segment) + return true; + return p1.Offset < y.offset; + }); + if (iter == addr_map.end()) + return llvm::None; + CVSymbol sym = syms.readRecord(*iter); + lldbassert(sym.kind() == S_PUB32); + PublicSym32 p; + llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym, p)); + if (p.Segment == addr.segment && p.Offset == addr.offset) + return p; + return llvm::None; } clang::Decl *PdbAstBuilder::GetOrCreateSymbolForId(PdbCompilandSymId id) { CVSymbol cvs = m_index.ReadSymbolRecord(id); + if (isLocalVariableType(cvs.kind())) { + clang::DeclContext *scope = GetParentDeclContext(id); + clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope); + PdbCompilandSymId scope_id(id.modi, m_decl_to_status[scope_decl].uid); + return GetOrCreateVariableDecl(scope_id, id); + } + switch (cvs.kind()) { case S_GPROC32: case S_LPROC32: @@ -333,14 +466,6 @@ clang::Decl *PdbAstBuilder::GetOrCreateSymbolForId(PdbCompilandSymId id) { return nullptr; case S_BLOCK32: return GetOrCreateBlockDecl(id); - case S_REGISTER: - case S_REGREL32: - case S_LOCAL: { - clang::DeclContext *scope = GetParentDeclContext(id); - clang::Decl *scope_decl = clang::Decl::castFromDeclContext(scope); - PdbCompilandSymId scope_id(id.modi, m_decl_to_status[scope_decl].uid); - return GetOrCreateLocalVariableDecl(scope_id, id); - } default: return nullptr; } @@ -371,6 +496,11 @@ clang::Decl *PdbAstBuilder::GetOrCreateDeclForUid(PdbSymUid uid) { } clang::DeclContext *PdbAstBuilder::GetOrCreateDeclContextForUid(PdbSymUid uid) { + if (uid.kind() == PdbSymUidKind::CompilandSym) { + if (uid.asCompilandSym().offset == 0) + return &GetTranslationUnitDecl(); + } + clang::Decl *decl = GetOrCreateDeclForUid(uid); if (!decl) return nullptr; @@ -385,14 +515,60 @@ clang::DeclContext *PdbAstBuilder::GetParentDeclContext(PdbSymUid uid) { case PdbSymUidKind::CompilandSym: { llvm::Optional<PdbCompilandSymId> scope = FindSymbolScope(m_index, uid.asCompilandSym()); - if (!scope) + if (scope) + return GetOrCreateDeclContextForUid(*scope); + + CVSymbol sym = m_index.ReadSymbolRecord(uid.asCompilandSym()); + if (!SymbolHasAddress(sym)) return &GetTranslationUnitDecl(); - return GetOrCreateDeclContextForUid(*scope); + SegmentOffset addr = GetSegmentAndOffset(sym); + llvm::Optional<PublicSym32> pub = + FindPublicSym(addr, m_index.symrecords(), m_index.publics()); + if (!pub) + return &GetTranslationUnitDecl(); + + llvm::ms_demangle::Demangler demangler; + StringView name{pub->Name.begin(), pub->Name.size()}; + llvm::ms_demangle::SymbolNode *node = demangler.parse(name); + if (!node) + return &GetTranslationUnitDecl(); + llvm::ArrayRef<llvm::ms_demangle::Node *> name_components{ + node->Name->Components->Nodes, node->Name->Components->Count - 1}; + + if (!name_components.empty()) { + // Render the current list of scope nodes as a fully qualified name, and + // look it up in the debug info as a type name. If we find something, + // this is a type (which may itself be prefixed by a namespace). If we + // don't, this is a list of namespaces. + std::string qname = RenderScopeList(name_components); + std::vector<TypeIndex> matches = m_index.tpi().findRecordsByName(qname); + while (!matches.empty()) { + clang::QualType qt = GetOrCreateType(matches.back()); + clang::TagDecl *tag = qt->getAsTagDecl(); + if (tag) + return clang::TagDecl::castToDeclContext(tag); + matches.pop_back(); + } + } + + // It's not a type. It must be a series of namespaces. + clang::DeclContext *context = &GetTranslationUnitDecl(); + while (!name_components.empty()) { + std::string ns = name_components.front()->toString(); + context = m_clang.GetUniqueNamespaceDeclaration(ns.c_str(), context); + name_components = name_components.drop_front(); + } + return context; } - case PdbSymUidKind::Type: + case PdbSymUidKind::Type: { // It could be a namespace, class, or global. We don't support nested // functions yet. Anyway, we just need to consult the parent type map. - break; + PdbTypeSymId type_id = uid.asTypeSym(); + auto iter = m_parent_types.find(type_id.index); + if (iter == m_parent_types.end()) + return &GetTranslationUnitDecl(); + return GetOrCreateDeclContextForUid(PdbTypeSymId(iter->second)); + } case PdbSymUidKind::FieldListMember: // In this case the parent DeclContext is the one for the class that this // member is inside of. @@ -531,10 +707,9 @@ PdbAstBuilder::CreateModifierType(const ModifierRecord &modifier) { clang::QualType PdbAstBuilder::CreateRecordType(PdbTypeSymId id, const TagRecord &record) { - clang::DeclContext *decl_context = nullptr; + clang::DeclContext *context = nullptr; std::string uname; - std::tie(decl_context, uname) = CreateDeclInfoForType(record, id.index); - + std::tie(context, uname) = CreateDeclInfoForType(record, id.index); clang::TagTypeKind ttk = TranslateUdtKind(record); lldb::AccessType access = (ttk == clang::TTK_Class) ? lldb::eAccessPrivate : lldb::eAccessPublic; @@ -544,7 +719,7 @@ clang::QualType PdbAstBuilder::CreateRecordType(PdbTypeSymId id, metadata.SetIsDynamicCXXType(false); CompilerType ct = - m_clang.CreateRecordType(decl_context, access, uname.c_str(), ttk, + m_clang.CreateRecordType(context, access, uname.c_str(), ttk, lldb::eLanguageTypeC_plus_plus, &metadata); lldbassert(ct.IsValid()); @@ -583,6 +758,12 @@ PdbAstBuilder::GetOrCreateBlockDecl(PdbCompilandSymId block_id) { clang::BlockDecl *block_decl = m_clang.CreateBlockDeclaration(scope); m_uid_to_decl.insert({toOpaqueUid(block_id), block_decl}); + + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(block_id); + m_decl_to_status.insert({block_decl, status}); + return block_decl; } @@ -595,12 +776,16 @@ clang::VarDecl *PdbAstBuilder::CreateVariableDecl(PdbSymUid uid, CVSymbol sym, &scope, var_info.name.str().c_str(), qt); m_uid_to_decl[toOpaqueUid(uid)] = var_decl; + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(uid); + m_decl_to_status.insert({var_decl, status}); return var_decl; } clang::VarDecl * -PdbAstBuilder::GetOrCreateLocalVariableDecl(PdbCompilandSymId scope_id, - PdbCompilandSymId var_id) { +PdbAstBuilder::GetOrCreateVariableDecl(PdbCompilandSymId scope_id, + PdbCompilandSymId var_id) { if (clang::Decl *decl = TryGetDecl(var_id)) return llvm::dyn_cast<clang::VarDecl>(decl); @@ -610,8 +795,7 @@ PdbAstBuilder::GetOrCreateLocalVariableDecl(PdbCompilandSymId scope_id, return CreateVariableDecl(PdbSymUid(var_id), sym, *scope); } -clang::VarDecl * -PdbAstBuilder::GetOrCreateGlobalVariableDecl(PdbGlobalSymId var_id) { +clang::VarDecl *PdbAstBuilder::GetOrCreateVariableDecl(PdbGlobalSymId var_id) { if (clang::Decl *decl = TryGetDecl(var_id)) return llvm::dyn_cast<clang::VarDecl>(decl); @@ -706,6 +890,12 @@ PdbAstBuilder::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) { return llvm::dyn_cast<clang::FunctionDecl>(decl); clang::DeclContext *parent = GetParentDeclContext(PdbSymUid(func_id)); + std::string context_name; + if (clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(parent)) { + context_name = ns->getQualifiedNameAsString(); + } else if (clang::TagDecl *tag = llvm::dyn_cast<clang::TagDecl>(parent)) { + context_name = tag->getQualifiedNameAsString(); + } CVSymbol cvs = m_index.ReadSymbolRecord(func_id); ProcSym proc(static_cast<SymbolRecordKind>(cvs.kind())); @@ -713,6 +903,8 @@ PdbAstBuilder::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) { PdbTypeSymId type_id(proc.FunctionType); clang::QualType qt = GetOrCreateType(type_id); + if (qt.isNull()) + return nullptr; clang::StorageClass storage = clang::SC_None; if (proc.Kind == SymbolRecordKind::ProcSym) @@ -723,11 +915,19 @@ PdbAstBuilder::GetOrCreateFunctionDecl(PdbCompilandSymId func_id) { CompilerType func_ct = ToCompilerType(qt); + llvm::StringRef proc_name = proc.Name; + proc_name.consume_front(context_name); + proc_name.consume_front("::"); + clang::FunctionDecl *function_decl = m_clang.CreateFunctionDeclaration( - parent, proc.Name.str().c_str(), func_ct, storage, false); + parent, proc_name.str().c_str(), func_ct, storage, false); lldbassert(m_uid_to_decl.count(toOpaqueUid(func_id)) == 0); m_uid_to_decl[toOpaqueUid(func_id)] = function_decl; + DeclStatus status; + status.resolved = true; + status.uid = toOpaqueUid(func_id); + m_decl_to_status.insert({function_decl, status}); CreateFunctionParameters(func_id, *function_decl, func_type->getNumParams()); @@ -866,6 +1066,180 @@ PdbAstBuilder::CreateProcedureType(const ProcedureRecord &proc) { func_sig_ast_type.GetOpaqueQualType()); } +static bool isTagDecl(clang::DeclContext &context) { + return !!llvm::dyn_cast<clang::TagDecl>(&context); +} + +static bool isFunctionDecl(clang::DeclContext &context) { + return !!llvm::dyn_cast<clang::FunctionDecl>(&context); +} + +static bool isBlockDecl(clang::DeclContext &context) { + return !!llvm::dyn_cast<clang::BlockDecl>(&context); +} + +void PdbAstBuilder::ParseAllNamespacesPlusChildrenOf( + llvm::Optional<llvm::StringRef> parent) { + TypeIndex ti{m_index.tpi().TypeIndexBegin()}; + for (const CVType &cvt : m_index.tpi().typeArray()) { + PdbTypeSymId tid{ti}; + ++ti; + + if (!IsTagRecord(cvt)) + continue; + + CVTagRecord tag = CVTagRecord::create(cvt); + + if (!parent.hasValue()) { + clang::QualType qt = GetOrCreateType(tid); + CompleteType(qt); + continue; + } + + // Call CreateDeclInfoForType unconditionally so that the namespace info + // gets created. But only call CreateRecordType if the namespace name + // matches. + clang::DeclContext *context = nullptr; + std::string uname; + std::tie(context, uname) = CreateDeclInfoForType(tag.asTag(), tid.index); + if (!context->isNamespace()) + continue; + + clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(context); + std::string actual_ns = ns->getQualifiedNameAsString(); + if (llvm::StringRef(actual_ns).startswith(*parent)) { + clang::QualType qt = GetOrCreateType(tid); + CompleteType(qt); + continue; + } + } + + uint32_t module_count = m_index.dbi().modules().getModuleCount(); + for (uint16_t modi = 0; modi < module_count; ++modi) { + CompilandIndexItem &cii = m_index.compilands().GetOrCreateCompiland(modi); + const CVSymbolArray &symbols = cii.m_debug_stream.getSymbolArray(); + auto iter = symbols.begin(); + while (iter != symbols.end()) { + PdbCompilandSymId sym_id{modi, iter.offset()}; + + switch (iter->kind()) { + case S_GPROC32: + case S_LPROC32: + GetOrCreateFunctionDecl(sym_id); + iter = symbols.at(getScopeEndOffset(*iter)); + break; + case S_GDATA32: + case S_GTHREAD32: + case S_LDATA32: + case S_LTHREAD32: + GetOrCreateVariableDecl(PdbCompilandSymId(modi, 0), sym_id); + ++iter; + break; + default: + ++iter; + continue; + } + } + } +} + +static CVSymbolArray skipFunctionParameters(clang::Decl &decl, + const CVSymbolArray &symbols) { + clang::FunctionDecl *func_decl = llvm::dyn_cast<clang::FunctionDecl>(&decl); + if (!func_decl) + return symbols; + unsigned int params = func_decl->getNumParams(); + if (params == 0) + return symbols; + + CVSymbolArray result = symbols; + + while (!result.empty()) { + if (params == 0) + return result; + + CVSymbol sym = *result.begin(); + result.drop_front(); + + if (!isLocalVariableType(sym.kind())) + continue; + + --params; + } + return result; +} + +void PdbAstBuilder::ParseBlockChildren(PdbCompilandSymId block_id) { + CVSymbol sym = m_index.ReadSymbolRecord(block_id); + lldbassert(sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32 || + sym.kind() == S_BLOCK32); + CompilandIndexItem &cii = + m_index.compilands().GetOrCreateCompiland(block_id.modi); + CVSymbolArray symbols = + cii.m_debug_stream.getSymbolArrayForScope(block_id.offset); + + // Function parameters should already have been created when the function was + // parsed. + if (sym.kind() == S_GPROC32 || sym.kind() == S_LPROC32) + symbols = + skipFunctionParameters(*m_uid_to_decl[toOpaqueUid(block_id)], symbols); + + auto begin = symbols.begin(); + while (begin != symbols.end()) { + PdbCompilandSymId child_sym_id(block_id.modi, begin.offset()); + GetOrCreateSymbolForId(child_sym_id); + if (begin->kind() == S_BLOCK32) { + ParseBlockChildren(child_sym_id); + begin = symbols.at(getScopeEndOffset(*begin)); + } + ++begin; + } +} + +void PdbAstBuilder::ParseDeclsForSimpleContext(clang::DeclContext &context) { + + clang::Decl *decl = clang::Decl::castFromDeclContext(&context); + lldbassert(decl); + + auto iter = m_decl_to_status.find(decl); + lldbassert(iter != m_decl_to_status.end()); + + if (auto *tag = llvm::dyn_cast<clang::TagDecl>(&context)) { + CompleteTagDecl(*tag); + return; + } + + if (isFunctionDecl(context) || isBlockDecl(context)) { + PdbCompilandSymId block_id = PdbSymUid(iter->second.uid).asCompilandSym(); + ParseBlockChildren(block_id); + } +} + +void PdbAstBuilder::ParseDeclsForContext(clang::DeclContext &context) { + // Namespaces aren't explicitly represented in the debug info, and the only + // way to parse them is to parse all type info, demangling every single type + // and trying to reconstruct the DeclContext hierarchy this way. Since this + // is an expensive operation, we have to special case it so that we do other + // work (such as parsing the items that appear within the namespaces) at the + // same time. + if (context.isTranslationUnit()) { + ParseAllNamespacesPlusChildrenOf(llvm::None); + return; + } + + if (context.isNamespace()) { + clang::NamespaceDecl &ns = *llvm::dyn_cast<clang::NamespaceDecl>(&context); + std::string qname = ns.getQualifiedNameAsString(); + ParseAllNamespacesPlusChildrenOf(llvm::StringRef{qname}); + return; + } + + if (isTagDecl(context) || isFunctionDecl(context) || isBlockDecl(context)) { + ParseDeclsForSimpleContext(context); + return; + } +} + CompilerDecl PdbAstBuilder::ToCompilerDecl(clang::Decl &decl) { return {&m_clang, &decl}; } @@ -879,4 +1253,9 @@ PdbAstBuilder::ToCompilerDeclContext(clang::DeclContext &context) { return {&m_clang, &context}; } +clang::DeclContext * +PdbAstBuilder::FromCompilerDeclContext(CompilerDeclContext context) { + return static_cast<clang::DeclContext *>(context.GetOpaqueDeclContext()); +} + void PdbAstBuilder::Dump(Stream &stream) { m_clang.Dump(stream); } |

