| //===-- DebugNamesDWARFIndex.cpp -------------------------------*- C++ -*-===// |
| // |
| // 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/SymbolFileDWARFDwo.h" |
| #include "lldb/Utility/RegularExpression.h" |
| #include "lldb/Utility/Stream.h" |
| |
| using namespace lldb_private; |
| using namespace lldb; |
| |
| llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> |
| DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names, |
| DWARFDataExtractor debug_str, |
| DWARFDebugInfo *debug_info) { |
| if (!debug_info) { |
| return llvm::make_error<llvm::StringError>("debug info null", |
| llvm::inconvertibleErrorCode()); |
| } |
| auto index_up = llvm::make_unique<DebugNames>(debug_names.GetAsLLVM(), |
| 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, *debug_info)); |
| } |
| |
| llvm::DenseSet<dw_offset_t> |
| DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) { |
| llvm::DenseSet<dw_offset_t> result; |
| for (const DebugNames::NameIndex &ni : debug_names) { |
| for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu) |
| result.insert(ni.getCUOffset(cu)); |
| } |
| return result; |
| } |
| |
| llvm::Optional<DIERef> |
| DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) { |
| llvm::Optional<uint64_t> cu_offset = entry.getCUOffset(); |
| if (!cu_offset) |
| return llvm::None; |
| |
| DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset); |
| if (!cu) |
| return llvm::None; |
| |
| // This initializes the DWO symbol file. It's not possible for |
| // GetDwoSymbolFile to call this automatically because of mutual recursion |
| // between this and DWARFDebugInfoEntry::GetAttributeValue. |
| cu->ExtractUnitDIEIfNeeded(); |
| cu = &cu->GetNonSkeletonUnit(); |
| |
| if (llvm::Optional<uint64_t> die_offset = entry.getDIEUnitOffset()) |
| return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(), |
| DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset); |
| |
| return llvm::None; |
| } |
| |
| void DebugNamesDWARFIndex::Append(const DebugNames::Entry &entry, |
| DIEArray &offsets) { |
| if (llvm::Optional<DIERef> ref = ToDIERef(entry)) |
| offsets.push_back(*ref); |
| } |
| |
| void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error, |
| const DebugNames::NameIndex &ni, |
| llvm::StringRef name) { |
| // Ignore SentinelErrors, log everything else. |
| LLDB_LOG_ERROR( |
| LogChannelDWARF::GetLogIfAll(DWARF_LOG_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, |
| DIEArray &offsets) { |
| m_fallback.GetGlobalVariables(basename, offsets); |
| |
| for (const DebugNames::Entry &entry : |
| m_debug_names_up->equal_range(basename.GetStringRef())) { |
| if (entry.tag() != DW_TAG_variable) |
| continue; |
| |
| Append(entry, offsets); |
| } |
| } |
| |
| void DebugNamesDWARFIndex::GetGlobalVariables(const RegularExpression ®ex, |
| DIEArray &offsets) { |
| m_fallback.GetGlobalVariables(regex, offsets); |
| |
| for (const DebugNames::NameIndex &ni: *m_debug_names_up) { |
| for (DebugNames::NameTableEntry nte: ni) { |
| if (!regex.Execute(nte.getString())) |
| continue; |
| |
| uint32_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; |
| |
| Append(*entry_or, offsets); |
| } |
| MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); |
| } |
| } |
| } |
| |
| void DebugNamesDWARFIndex::GetGlobalVariables(const DWARFUnit &cu, |
| DIEArray &offsets) { |
| m_fallback.GetGlobalVariables(cu, offsets); |
| |
| uint64_t cu_offset = cu.GetOffset(); |
| for (const DebugNames::NameIndex &ni: *m_debug_names_up) { |
| for (DebugNames::NameTableEntry nte: ni) { |
| uint32_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; |
| |
| Append(*entry_or, offsets); |
| } |
| MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); |
| } |
| } |
| } |
| |
| void DebugNamesDWARFIndex::GetCompleteObjCClass(ConstString class_name, |
| bool must_be_implementation, |
| DIEArray &offsets) { |
| m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, offsets); |
| |
| // Keep a list of incomplete types as fallback for when we don't find the |
| // complete type. |
| DIEArray 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; |
| |
| llvm::Optional<DIERef> ref = ToDIERef(entry); |
| if (!ref) |
| continue; |
| |
| DWARFUnit *cu = m_debug_info.GetUnit(*ref); |
| if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) { |
| incomplete_types.push_back(*ref); |
| continue; |
| } |
| |
| // FIXME: We should return DWARFDIEs so we don't have to resolve it twice. |
| DWARFDIE die = m_debug_info.GetDIE(*ref); |
| if (!die) |
| continue; |
| |
| if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) { |
| // If we find the complete version we're done. |
| offsets.push_back(*ref); |
| return; |
| } else { |
| incomplete_types.push_back(*ref); |
| } |
| } |
| |
| offsets.insert(offsets.end(), incomplete_types.begin(), |
| incomplete_types.end()); |
| } |
| |
| void DebugNamesDWARFIndex::GetTypes(ConstString name, DIEArray &offsets) { |
| m_fallback.GetTypes(name, offsets); |
| |
| for (const DebugNames::Entry &entry : |
| m_debug_names_up->equal_range(name.GetStringRef())) { |
| if (isType(entry.tag())) |
| Append(entry, offsets); |
| } |
| } |
| |
| void DebugNamesDWARFIndex::GetTypes(const DWARFDeclContext &context, |
| DIEArray &offsets) { |
| m_fallback.GetTypes(context, offsets); |
| |
| for (const DebugNames::Entry &entry : |
| m_debug_names_up->equal_range(context[0].name)) { |
| if (entry.tag() == context[0].tag) |
| Append(entry, offsets); |
| } |
| } |
| |
| void DebugNamesDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) { |
| m_fallback.GetNamespaces(name, offsets); |
| |
| for (const DebugNames::Entry &entry : |
| m_debug_names_up->equal_range(name.GetStringRef())) { |
| if (entry.tag() == DW_TAG_namespace) |
| Append(entry, offsets); |
| } |
| } |
| |
| void DebugNamesDWARFIndex::GetFunctions( |
| ConstString name, SymbolFileDWARF &dwarf, |
| const CompilerDeclContext &parent_decl_ctx, uint32_t name_type_mask, |
| std::vector<DWARFDIE> &dies) { |
| |
| std::vector<DWARFDIE> v; |
| m_fallback.GetFunctions(name, dwarf, parent_decl_ctx, name_type_mask, v); |
| |
| 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 (llvm::Optional<DIERef> ref = ToDIERef(entry)) |
| ProcessFunctionDIE(name.GetStringRef(), *ref, dwarf, parent_decl_ctx, |
| name_type_mask, v); |
| } |
| |
| std::set<DWARFDebugInfoEntry *> seen; |
| for (DWARFDIE die : v) |
| if (seen.insert(die.GetDIE()).second) |
| dies.push_back(die); |
| } |
| |
| void DebugNamesDWARFIndex::GetFunctions(const RegularExpression ®ex, |
| DIEArray &offsets) { |
| m_fallback.GetFunctions(regex, offsets); |
| |
| for (const DebugNames::NameIndex &ni: *m_debug_names_up) { |
| for (DebugNames::NameTableEntry nte: ni) { |
| if (!regex.Execute(nte.getString())) |
| continue; |
| |
| uint32_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; |
| |
| Append(*entry_or, offsets); |
| } |
| MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); |
| } |
| } |
| } |
| |
| 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(os.str()); |
| } |