//===-- 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/SymbolFileDWARFDwo.h" #include "lldb/Core/Module.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" #include using namespace lldb_private; using namespace lldb; using namespace lldb_private::dwarf; llvm::Expected> DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names, DWARFDataExtractor debug_str, SymbolFileDWARF &dwarf) { auto index_up = std::make_unique(debug_names.GetAsLLVM(), debug_str.GetAsLLVM()); if (llvm::Error E = index_up->extract()) return std::move(E); return std::unique_ptr(new DebugNamesDWARFIndex( module, std::move(index_up), debug_names, debug_str, dwarf)); } llvm::DenseSet DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) { llvm::DenseSet result; for (const DebugNames::NameIndex &ni : debug_names) { for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu) result.insert(ni.getCUOffset(cu)); } return result; } std::optional DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) { std::optional cu_offset = entry.getCUOffset(); if (!cu_offset) return std::nullopt; DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset); if (!cu) return std::nullopt; cu = &cu->GetNonSkeletonUnit(); if (std::optional die_offset = entry.getDIEUnitOffset()) return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(), DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset); return std::nullopt; } bool DebugNamesDWARFIndex::ProcessEntry( const DebugNames::Entry &entry, llvm::function_ref callback, llvm::StringRef name) { std::optional ref = ToDIERef(entry); if (!ref) return true; SymbolFileDWARF &dwarf = *llvm::cast( m_module.GetSymbolFile()->GetBackingSymbolFile()); DWARFDIE die = dwarf.GetDIE(*ref); if (!die) return true; 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 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, basename.GetStringRef())) return; } m_fallback.GetGlobalVariables(basename, callback); } void DebugNamesDWARFIndex::GetGlobalVariables( const RegularExpression ®ex, llvm::function_ref 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 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, llvm::StringRef(nte.getString()))) return; } MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); } } m_fallback.GetGlobalVariables(regex, callback); } void DebugNamesDWARFIndex::GetGlobalVariables( DWARFUnit &cu, llvm::function_ref callback) { lldbassert(!cu.GetSymbolFileDWARF().GetDwoNum()); uint64_t cu_offset = cu.GetOffset(); bool found_entry_for_cu = false; for (const DebugNames::NameIndex &ni: *m_debug_names_up) { for (DebugNames::NameTableEntry nte: ni) { uint64_t entry_offset = nte.getEntryOffset(); llvm::Expected 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, llvm::StringRef(nte.getString()))) 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 callback) { // 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; std::optional 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; } DWARFDIE die = m_debug_info.GetDIE(*ref); if (!die) { ReportInvalidDIERef(*ref, class_name.GetStringRef()); 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(*ref); } auto dierefcallback = DIERefCallback(callback, class_name.GetStringRef()); for (DIERef ref : incomplete_types) if (!dierefcallback(ref)) return; m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback); } void DebugNamesDWARFIndex::GetTypes( ConstString name, llvm::function_ref callback) { for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name.GetStringRef())) { if (isType(entry.tag())) { if (!ProcessEntry(entry, callback, name.GetStringRef())) return; } } m_fallback.GetTypes(name, callback); } void DebugNamesDWARFIndex::GetTypes( const DWARFDeclContext &context, llvm::function_ref 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, name)) return; } } m_fallback.GetTypes(context, callback); } void DebugNamesDWARFIndex::GetNamespaces( ConstString name, llvm::function_ref callback) { for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name.GetStringRef())) { if (entry.tag() == DW_TAG_namespace) { if (!ProcessEntry(entry, callback, name.GetStringRef())) return; } } m_fallback.GetNamespaces(name, callback); } void DebugNamesDWARFIndex::GetFunctions( const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, llvm::function_ref callback) { ConstString name = lookup_info.GetLookupName(); std::set 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 (std::optional ref = ToDIERef(entry)) { if (!ProcessFunctionDIE(lookup_info, *ref, dwarf, parent_decl_ctx, [&](DWARFDIE die) { if (!seen.insert(die.GetDIE()).second) return true; return callback(die); })) return; } } m_fallback.GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback); } void DebugNamesDWARFIndex::GetFunctions( const RegularExpression ®ex, llvm::function_ref 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 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, llvm::StringRef(nte.getString()))) 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(os.str()); }