| //===-- DebugNamesDWARFIndex.cpp ------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h" | |
| #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" | |
| #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h" | |
| #include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h" | |
| #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Utility/RegularExpression.h" | |
| #include "lldb/Utility/Stream.h" | |
| #include "lldb/lldb-private-enumerations.h" | |
| #include "llvm/ADT/Sequence.h" | |
| #include <optional> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| using namespace lldb_private::plugin::dwarf; | |
| using namespace llvm::dwarf; | |
| llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> | |
| DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names, | |
| DWARFDataExtractor debug_str, | |
| SymbolFileDWARF &dwarf) { | |
| auto index_up = std::make_unique<DebugNames>(debug_names.GetAsLLVMDWARF(), | |
| debug_str.GetAsLLVM()); | |
| if (llvm::Error E = index_up->extract()) | |
| return std::move(E); | |
| return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex( | |
| module, std::move(index_up), debug_names, debug_str, dwarf)); | |
| } | |
| llvm::DenseSet<uint64_t> | |
| DebugNamesDWARFIndex::GetTypeUnitSignatures(const DebugNames &debug_names) { | |
| llvm::DenseSet<uint64_t> result; | |
| for (const DebugNames::NameIndex &ni : debug_names) { | |
| const uint32_t num_tus = ni.getForeignTUCount(); | |
| for (uint32_t tu = 0; tu < num_tus; ++tu) | |
| result.insert(ni.getForeignTUSignature(tu)); | |
| } | |
| return result; | |
| } | |
| llvm::DenseSet<dw_offset_t> | |
| DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) { | |
| llvm::DenseSet<dw_offset_t> result; | |
| for (const DebugNames::NameIndex &ni : debug_names) { | |
| const uint32_t num_cus = ni.getCUCount(); | |
| for (uint32_t cu = 0; cu < num_cus; ++cu) | |
| result.insert(ni.getCUOffset(cu)); | |
| const uint32_t num_tus = ni.getLocalTUCount(); | |
| for (uint32_t tu = 0; tu < num_tus; ++tu) | |
| result.insert(ni.getLocalTUOffset(tu)); | |
| } | |
| return result; | |
| } | |
| std::optional<DWARFTypeUnit *> | |
| DebugNamesDWARFIndex::GetForeignTypeUnit(const DebugNames::Entry &entry) const { | |
| std::optional<uint64_t> type_sig = entry.getForeignTUTypeSignature(); | |
| if (!type_sig.has_value()) | |
| return std::nullopt; | |
| // Ask the entry for the skeleton compile unit offset and fetch the .dwo | |
| // file from it and get the type unit by signature from there. If we find | |
| // the type unit in the .dwo file, we don't need to check that the | |
| // DW_AT_dwo_name matches because each .dwo file can have its own type unit. | |
| std::optional<uint64_t> cu_offset = entry.getRelatedCUOffset(); | |
| if (!cu_offset) | |
| return nullptr; // Return NULL, this is a type unit, but couldn't find it. | |
| DWARFUnit *cu = | |
| m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset); | |
| if (!cu) | |
| return nullptr; // Return NULL, this is a type unit, but couldn't find it. | |
| auto dwp_sp = m_debug_info.GetDwpSymbolFile(); | |
| if (!dwp_sp) { | |
| // No .dwp file, we need to load the .dwo file. | |
| DWARFUnit &dwo_cu = cu->GetNonSkeletonUnit(); | |
| // We don't need the check if the type unit matches the .dwo file if we have | |
| // a .dwo file (not a .dwp), so we can just return the value here. | |
| if (!dwo_cu.IsDWOUnit()) | |
| return nullptr; // We weren't able to load the .dwo file. | |
| return dwo_cu.GetSymbolFileDWARF().DebugInfo().GetTypeUnitForHash( | |
| *type_sig); | |
| } | |
| // We have a .dwp file, just get the type unit from there. We need to verify | |
| // that the type unit that ended up in the final .dwp file is the right type | |
| // unit. Type units have signatures which are the same across multiple .dwo | |
| // files, but only one of those type units will end up in the .dwp file. The | |
| // contents of type units for the same type can be different in different .dwo | |
| // files, which means the DIE offsets might not be the same between two | |
| // different type units. So we need to determine if this accelerator table | |
| // matches the type unit that ended up in the .dwp file. If it doesn't match, | |
| // then we need to ignore this accelerator table entry as the type unit that | |
| // is in the .dwp file will have its own index. In order to determine if the | |
| // type unit that ended up in a .dwp file matches this DebugNames::Entry, we | |
| // need to find the skeleton compile unit for this entry. | |
| DWARFTypeUnit *foreign_tu = dwp_sp->DebugInfo().GetTypeUnitForHash(*type_sig); | |
| if (!foreign_tu) | |
| return nullptr; // Return NULL, this is a type unit, but couldn't find it. | |
| DWARFBaseDIE cu_die = cu->GetUnitDIEOnly(); | |
| DWARFBaseDIE tu_die = foreign_tu->GetUnitDIEOnly(); | |
| llvm::StringRef cu_dwo_name = | |
| cu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr); | |
| llvm::StringRef tu_dwo_name = | |
| tu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr); | |
| if (cu_dwo_name == tu_dwo_name) | |
| return foreign_tu; // We found a match! | |
| return nullptr; // Return NULL, this is a type unit, but couldn't find it. | |
| } | |
| DWARFUnit * | |
| DebugNamesDWARFIndex::GetNonSkeletonUnit(const DebugNames::Entry &entry) const { | |
| if (std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry)) | |
| return foreign_tu.value(); | |
| // Look for a DWARF unit offset (CU offset or local TU offset) as they are | |
| // both offsets into the .debug_info section. | |
| std::optional<uint64_t> unit_offset = entry.getCUOffset(); | |
| if (!unit_offset) | |
| unit_offset = entry.getLocalTUOffset(); | |
| if (unit_offset) { | |
| if (DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, | |
| *unit_offset)) | |
| return &cu->GetNonSkeletonUnit(); | |
| } | |
| return nullptr; | |
| } | |
| DWARFDIE DebugNamesDWARFIndex::GetDIE(const DebugNames::Entry &entry) const { | |
| DWARFUnit *unit = GetNonSkeletonUnit(entry); | |
| std::optional<uint64_t> die_offset = entry.getDIEUnitOffset(); | |
| if (!unit || !die_offset) | |
| return DWARFDIE(); | |
| if (DWARFDIE die = unit->GetDIE(unit->GetOffset() + *die_offset)) | |
| return die; | |
| m_module.ReportErrorIfModifyDetected( | |
| "the DWARF debug information has been modified (bad offset {0:x} in " | |
| "debug_names section)\n", | |
| *die_offset); | |
| return DWARFDIE(); | |
| } | |
| IterationAction DebugNamesDWARFIndex::ProcessEntry( | |
| const DebugNames::Entry &entry, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| DWARFDIE die = GetDIE(entry); | |
| if (!die) | |
| return IterationAction::Continue; | |
| // Clang used to erroneously emit index entries for declaration DIEs in case | |
| // when the definition is in a type unit (llvm.org/pr77696). | |
| if (die.IsStructUnionOrClass() && | |
| die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0)) | |
| return IterationAction::Continue; | |
| return callback(die); | |
| } | |
| void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error, | |
| const DebugNames::NameIndex &ni, | |
| llvm::StringRef name) { | |
| // Ignore SentinelErrors, log everything else. | |
| LLDB_LOG_ERROR( | |
| GetLog(DWARFLog::Lookups), | |
| handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}), | |
| "Failed to parse index entries for index at {1:x}, name {2}: {0}", | |
| ni.getUnitOffset(), name); | |
| } | |
| void DebugNamesDWARFIndex::GetGlobalVariables( | |
| ConstString basename, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| for (const DebugNames::Entry &entry : | |
| m_debug_names_up->equal_range(basename.GetStringRef())) { | |
| if (entry.tag() != DW_TAG_variable) | |
| continue; | |
| if (ProcessEntry(entry, callback) == IterationAction::Stop) | |
| return; | |
| } | |
| m_fallback.GetGlobalVariables(basename, callback); | |
| } | |
| void DebugNamesDWARFIndex::GetGlobalVariables( | |
| const RegularExpression ®ex, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| for (const DebugNames::NameIndex &ni: *m_debug_names_up) { | |
| for (DebugNames::NameTableEntry nte: ni) { | |
| Mangled mangled_name(nte.getString()); | |
| if (!mangled_name.NameMatches(regex)) | |
| continue; | |
| uint64_t entry_offset = nte.getEntryOffset(); | |
| llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); | |
| for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { | |
| if (entry_or->tag() != DW_TAG_variable) | |
| continue; | |
| if (ProcessEntry(*entry_or, callback) == IterationAction::Stop) | |
| return; | |
| } | |
| MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); | |
| } | |
| } | |
| m_fallback.GetGlobalVariables(regex, callback); | |
| } | |
| void DebugNamesDWARFIndex::GetGlobalVariables( | |
| DWARFUnit &cu, llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| uint64_t cu_offset = cu.GetOffset(); | |
| bool found_entry_for_cu = false; | |
| for (const DebugNames::NameIndex &ni : *m_debug_names_up) { | |
| // Check if this name index contains an entry for the given CU. | |
| bool cu_matches = false; | |
| for (uint32_t i = 0; i < ni.getCUCount(); ++i) { | |
| if (ni.getCUOffset(i) == cu_offset) { | |
| cu_matches = true; | |
| break; | |
| } | |
| } | |
| if (!cu_matches) | |
| continue; | |
| for (DebugNames::NameTableEntry nte : ni) { | |
| uint64_t entry_offset = nte.getEntryOffset(); | |
| llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); | |
| for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { | |
| if (entry_or->tag() != DW_TAG_variable) | |
| continue; | |
| if (entry_or->getCUOffset() != cu_offset) | |
| continue; | |
| found_entry_for_cu = true; | |
| if (ProcessEntry(*entry_or, callback) == IterationAction::Stop) | |
| return; | |
| } | |
| MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); | |
| } | |
| } | |
| // If no name index for that particular CU was found, fallback to | |
| // creating the manual index. | |
| if (!found_entry_for_cu) | |
| m_fallback.GetGlobalVariables(cu, callback); | |
| } | |
| void DebugNamesDWARFIndex::GetCompleteObjCClass( | |
| ConstString class_name, bool must_be_implementation, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| // Keep a list of incomplete types as fallback for when we don't find the | |
| // complete type. | |
| std::vector<DWARFDIE> incomplete_types; | |
| for (const DebugNames::Entry &entry : | |
| m_debug_names_up->equal_range(class_name.GetStringRef())) { | |
| if (entry.tag() != DW_TAG_structure_type && | |
| entry.tag() != DW_TAG_class_type) | |
| continue; | |
| DWARFDIE die = GetDIE(entry); | |
| if (!die) { | |
| // Report invalid | |
| continue; | |
| } | |
| if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) { | |
| // If we find the complete version we're done. | |
| callback(die); | |
| return; | |
| } | |
| incomplete_types.push_back(die); | |
| } | |
| for (DWARFDIE die : incomplete_types) | |
| if (callback(die) == IterationAction::Stop) | |
| return; | |
| m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback); | |
| } | |
| namespace { | |
| using Entry = llvm::DWARFDebugNames::Entry; | |
| /// If `entry` and all of its parents have an `IDX_parent`, use that information | |
| /// to build and return a list of at most `max_parents` parent Entries. | |
| /// `entry` itself is not included in the list. | |
| /// If any parent does not have an `IDX_parent`, or the Entry data is corrupted, | |
| /// nullopt is returned. | |
| std::optional<llvm::SmallVector<Entry, 4>> | |
| getParentChain(Entry entry, | |
| uint32_t max_parents = std::numeric_limits<uint32_t>::max()) { | |
| llvm::SmallVector<Entry, 4> parent_entries; | |
| do { | |
| if (!entry.hasParentInformation()) | |
| return std::nullopt; | |
| llvm::Expected<std::optional<Entry>> parent = entry.getParentDIEEntry(); | |
| if (!parent) { | |
| // Bad data. | |
| LLDB_LOG_ERROR( | |
| GetLog(DWARFLog::Lookups), parent.takeError(), | |
| "Failed to extract parent entry from a non-empty IDX_parent"); | |
| return std::nullopt; | |
| } | |
| // Last parent in the chain. | |
| if (!parent->has_value()) | |
| break; | |
| parent_entries.push_back(**parent); | |
| entry = **parent; | |
| } while (parent_entries.size() < max_parents); | |
| return parent_entries; | |
| } | |
| } // namespace | |
| void DebugNamesDWARFIndex::GetFullyQualifiedType( | |
| const DWARFDeclContext &context, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| if (context.GetSize() == 0) | |
| return; | |
| llvm::StringRef leaf_name = context[0].name; | |
| llvm::SmallVector<llvm::StringRef> parent_names; | |
| for (auto idx : llvm::seq<int>(1, context.GetSize())) | |
| parent_names.emplace_back(context[idx].name); | |
| // For each entry, grab its parent chain and check if we have a match. | |
| for (const DebugNames::Entry &entry : | |
| m_debug_names_up->equal_range(leaf_name)) { | |
| if (!isType(entry.tag())) | |
| continue; | |
| // If we get a NULL foreign_tu back, the entry doesn't match the type unit | |
| // in the .dwp file, or we were not able to load the .dwo file or the DWO ID | |
| // didn't match. | |
| std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry); | |
| if (foreign_tu && foreign_tu.value() == nullptr) | |
| continue; | |
| // Grab at most one extra parent, subsequent parents are not necessary to | |
| // test equality. | |
| std::optional<llvm::SmallVector<Entry, 4>> parent_chain = | |
| getParentChain(entry, parent_names.size() + 1); | |
| if (!parent_chain) { | |
| // Fallback: use the base class implementation. | |
| if (ProcessEntry(entry, [&](DWARFDIE die) { | |
| return GetFullyQualifiedTypeImpl(context, die, callback); | |
| }) == IterationAction::Stop) | |
| return; | |
| continue; | |
| } | |
| if (SameParentChain(parent_names, *parent_chain)) { | |
| if (ProcessEntry(entry, callback) == IterationAction::Stop) | |
| return; | |
| } | |
| } | |
| m_fallback.GetFullyQualifiedType(context, callback); | |
| } | |
| bool DebugNamesDWARFIndex::SameAsEntryContext( | |
| const CompilerContext &query_context, | |
| const DebugNames::Entry &entry) const { | |
| // TODO: check dwarf tag matches. | |
| // Peek at the AT_name of `entry` and test equality to `name`. | |
| auto maybe_dieoffset = entry.getDIEUnitOffset(); | |
| if (!maybe_dieoffset) | |
| return false; | |
| DWARFUnit *unit = GetNonSkeletonUnit(entry); | |
| if (!unit) | |
| return false; | |
| return query_context.name == | |
| unit->PeekDIEName(unit->GetOffset() + *maybe_dieoffset); | |
| } | |
| bool DebugNamesDWARFIndex::SameParentChain( | |
| llvm::ArrayRef<llvm::StringRef> parent_names, | |
| llvm::ArrayRef<DebugNames::Entry> parent_entries) const { | |
| if (parent_entries.size() != parent_names.size()) | |
| return false; | |
| auto SameAsEntryATName = [this](llvm::StringRef name, | |
| const DebugNames::Entry &entry) { | |
| // Peek at the AT_name of `entry` and test equality to `name`. | |
| auto maybe_dieoffset = entry.getDIEUnitOffset(); | |
| if (!maybe_dieoffset) | |
| return false; | |
| DWARFUnit *unit = GetNonSkeletonUnit(entry); | |
| if (!unit) | |
| return false; | |
| return name == unit->PeekDIEName(unit->GetOffset() + *maybe_dieoffset); | |
| }; | |
| // If the AT_name of any parent fails to match the expected name, we don't | |
| // have a match. | |
| for (auto [parent_name, parent_entry] : | |
| llvm::zip_equal(parent_names, parent_entries)) | |
| if (!SameAsEntryATName(parent_name, parent_entry)) | |
| return false; | |
| return true; | |
| } | |
| bool DebugNamesDWARFIndex::SameParentChain( | |
| llvm::ArrayRef<CompilerContext> parent_contexts, | |
| llvm::ArrayRef<DebugNames::Entry> parent_entries) const { | |
| if (parent_entries.size() != parent_contexts.size()) | |
| return false; | |
| // If the AT_name of any parent fails to match the expected name, we don't | |
| // have a match. | |
| for (auto [parent_context, parent_entry] : | |
| llvm::zip_equal(parent_contexts, parent_entries)) | |
| if (!SameAsEntryContext(parent_context, parent_entry)) | |
| return false; | |
| return true; | |
| } | |
| bool DebugNamesDWARFIndex::WithinParentChain( | |
| llvm::ArrayRef<CompilerContext> query_contexts, | |
| llvm::ArrayRef<DebugNames::Entry> parent_chain) const { | |
| if (query_contexts.size() == parent_chain.size()) | |
| return SameParentChain(query_contexts, parent_chain); | |
| // If parent chain does not have enough entries, we can't possibly have a | |
| // match. | |
| while (!query_contexts.empty() && | |
| query_contexts.size() <= parent_chain.size()) { | |
| if (SameAsEntryContext(query_contexts.front(), parent_chain.front())) { | |
| query_contexts = query_contexts.drop_front(); | |
| parent_chain = parent_chain.drop_front(); | |
| } else { | |
| // Name does not match, try next parent_chain entry if the current entry | |
| // is namespace because the current one can be an inline namespace. | |
| if (parent_chain.front().tag() != DW_TAG_namespace) | |
| return false; | |
| parent_chain = parent_chain.drop_front(); | |
| } | |
| } | |
| return query_contexts.empty(); | |
| } | |
| void DebugNamesDWARFIndex::GetTypes( | |
| ConstString name, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| for (const DebugNames::Entry &entry : | |
| m_debug_names_up->equal_range(name.GetStringRef())) { | |
| if (isType(entry.tag())) { | |
| if (ProcessEntry(entry, callback) == IterationAction::Stop) | |
| return; | |
| } | |
| } | |
| m_fallback.GetTypes(name, callback); | |
| } | |
| void DebugNamesDWARFIndex::GetTypes( | |
| const DWARFDeclContext &context, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| auto name = context[0].name; | |
| for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) { | |
| if (entry.tag() == context[0].tag) { | |
| if (ProcessEntry(entry, callback) == IterationAction::Stop) | |
| return; | |
| } | |
| } | |
| m_fallback.GetTypes(context, callback); | |
| } | |
| void DebugNamesDWARFIndex::GetNamespaces( | |
| ConstString name, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| for (const DebugNames::Entry &entry : | |
| m_debug_names_up->equal_range(name.GetStringRef())) { | |
| llvm::dwarf::Tag entry_tag = entry.tag(); | |
| if (entry_tag == DW_TAG_namespace || | |
| entry_tag == DW_TAG_imported_declaration) { | |
| if (ProcessEntry(entry, callback) == IterationAction::Stop) | |
| return; | |
| } | |
| } | |
| m_fallback.GetNamespaces(name, callback); | |
| } | |
| llvm::SmallVector<CompilerContext> | |
| DebugNamesDWARFIndex::GetTypeQueryParentContexts(TypeQuery &query) { | |
| std::vector<lldb_private::CompilerContext> &query_decl_context = | |
| query.GetContextRef(); | |
| llvm::SmallVector<CompilerContext> parent_contexts; | |
| if (!query_decl_context.empty()) { | |
| // Skip the last entry as it's the type we're matching parents for. | |
| // Reverse the query decl context to match parent chain order. | |
| llvm::ArrayRef<CompilerContext> parent_contexts_ref( | |
| query_decl_context.data(), query_decl_context.size() - 1); | |
| for (const CompilerContext &ctx : llvm::reverse(parent_contexts_ref)) { | |
| // Skip any context without name because .debug_names might not encode | |
| // them. (e.g. annonymous namespace) | |
| if ((ctx.kind & CompilerContextKind::AnyType) != | |
| CompilerContextKind::Invalid && | |
| !ctx.name.IsEmpty()) | |
| parent_contexts.push_back(ctx); | |
| } | |
| } | |
| return parent_contexts; | |
| } | |
| void DebugNamesDWARFIndex::GetTypesWithQuery( | |
| TypeQuery &query, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| ConstString name = query.GetTypeBasename(); | |
| std::vector<lldb_private::CompilerContext> query_context = | |
| query.GetContextRef(); | |
| if (query_context.size() <= 1 && !query.GetExactMatch()) | |
| return GetTypes(name, callback); | |
| llvm::SmallVector<CompilerContext> parent_contexts = | |
| GetTypeQueryParentContexts(query); | |
| // For each entry, grab its parent chain and check if we have a match. | |
| for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) { | |
| if (!isType(entry.tag())) | |
| continue; | |
| // If we get a NULL foreign_tu back, the entry doesn't match the type unit | |
| // in the .dwp file, or we were not able to load the .dwo file or the DWO ID | |
| // didn't match. | |
| std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry); | |
| if (foreign_tu && foreign_tu.value() == nullptr) | |
| continue; | |
| std::optional<llvm::SmallVector<Entry, 4>> parent_chain = | |
| getParentChain(entry); | |
| if (!parent_chain) { | |
| // Fallback: use the base class implementation. | |
| if (ProcessEntry(entry, [&](DWARFDIE die) { | |
| return ProcessTypeDIEMatchQuery(query, die, callback); | |
| }) == IterationAction::Stop) | |
| return; | |
| continue; | |
| } | |
| if (WithinParentChain(parent_contexts, *parent_chain)) { | |
| if (ProcessEntry(entry, [&](DWARFDIE die) { | |
| // After .debug_names filtering still sending to base | |
| // class for further filtering before calling the | |
| // callback. | |
| return ProcessTypeDIEMatchQuery(query, die, callback); | |
| }) == IterationAction::Stop) | |
| return; | |
| } | |
| } | |
| m_fallback.GetTypesWithQuery(query, callback); | |
| } | |
| void DebugNamesDWARFIndex::GetNamespacesWithParents( | |
| ConstString name, const CompilerDeclContext &parent_decl_ctx, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| std::vector<lldb_private::CompilerContext> parent_contexts = | |
| parent_decl_ctx.GetCompilerContext(); | |
| llvm::SmallVector<CompilerContext> parent_named_contexts; | |
| std::copy_if(parent_contexts.rbegin(), parent_contexts.rend(), | |
| std::back_inserter(parent_named_contexts), | |
| [](const CompilerContext &ctx) { return !ctx.name.IsEmpty(); }); | |
| for (const DebugNames::Entry &entry : | |
| m_debug_names_up->equal_range(name.GetStringRef())) { | |
| llvm::dwarf::Tag entry_tag = entry.tag(); | |
| if (entry_tag == DW_TAG_namespace || | |
| entry_tag == DW_TAG_imported_declaration) { | |
| std::optional<llvm::SmallVector<Entry, 4>> parent_chain = | |
| getParentChain(entry); | |
| if (!parent_chain) { | |
| // Fallback: use the base class implementation. | |
| if (ProcessEntry(entry, [&](DWARFDIE die) { | |
| return ProcessNamespaceDieMatchParents(parent_decl_ctx, die, | |
| callback); | |
| }) == IterationAction::Stop) | |
| return; | |
| continue; | |
| } | |
| if (WithinParentChain(parent_named_contexts, *parent_chain)) { | |
| if (ProcessEntry(entry, [&](DWARFDIE die) { | |
| // After .debug_names filtering still sending to | |
| // base class for further filtering before calling | |
| // the callback. | |
| return ProcessNamespaceDieMatchParents(parent_decl_ctx, die, | |
| callback); | |
| }) == IterationAction::Stop) | |
| return; | |
| } | |
| } | |
| } | |
| m_fallback.GetNamespacesWithParents(name, parent_decl_ctx, callback); | |
| } | |
| void DebugNamesDWARFIndex::GetFunctions( | |
| const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, | |
| const CompilerDeclContext &parent_decl_ctx, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| ConstString name = lookup_info.GetLookupName(); | |
| std::set<DWARFDebugInfoEntry *> seen; | |
| for (const DebugNames::Entry &entry : | |
| m_debug_names_up->equal_range(name.GetStringRef())) { | |
| Tag tag = entry.tag(); | |
| if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) | |
| continue; | |
| if (DWARFDIE die = GetDIE(entry)) { | |
| if (ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, | |
| [&](DWARFDIE die) { | |
| if (!seen.insert(die.GetDIE()).second) | |
| return IterationAction::Continue; | |
| return callback(die); | |
| }) == IterationAction::Stop) | |
| return; | |
| } | |
| } | |
| m_fallback.GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback); | |
| } | |
| void DebugNamesDWARFIndex::GetFunctions( | |
| const RegularExpression ®ex, | |
| llvm::function_ref<IterationAction(DWARFDIE die)> callback) { | |
| for (const DebugNames::NameIndex &ni: *m_debug_names_up) { | |
| for (DebugNames::NameTableEntry nte: ni) { | |
| if (!regex.Execute(nte.getString())) | |
| continue; | |
| uint64_t entry_offset = nte.getEntryOffset(); | |
| llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); | |
| for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { | |
| Tag tag = entry_or->tag(); | |
| if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) | |
| continue; | |
| if (ProcessEntry(*entry_or, callback) == IterationAction::Stop) | |
| return; | |
| } | |
| MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); | |
| } | |
| } | |
| m_fallback.GetFunctions(regex, callback); | |
| } | |
| void DebugNamesDWARFIndex::Dump(Stream &s) { | |
| m_fallback.Dump(s); | |
| std::string data; | |
| llvm::raw_string_ostream os(data); | |
| m_debug_names_up->dump(os); | |
| s.PutCString(data); | |
| } |