1 //===-- DebugNamesDWARFIndex.cpp ------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h" 10 #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" 11 #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h" 12 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" 13 #include "lldb/Core/Module.h" 14 #include "lldb/Utility/RegularExpression.h" 15 #include "lldb/Utility/Stream.h" 16 #include <optional> 17 18 using namespace lldb_private; 19 using namespace lldb; 20 using namespace lldb_private::dwarf; 21 22 llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> 23 DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names, 24 DWARFDataExtractor debug_str, 25 SymbolFileDWARF &dwarf) { 26 auto index_up = std::make_unique<DebugNames>(debug_names.GetAsLLVM(), 27 debug_str.GetAsLLVM()); 28 if (llvm::Error E = index_up->extract()) 29 return std::move(E); 30 31 return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex( 32 module, std::move(index_up), debug_names, debug_str, dwarf)); 33 } 34 35 llvm::DenseSet<dw_offset_t> 36 DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) { 37 llvm::DenseSet<dw_offset_t> result; 38 for (const DebugNames::NameIndex &ni : debug_names) { 39 for (uint32_t cu = 0; cu < ni.getCUCount(); ++cu) 40 result.insert(ni.getCUOffset(cu)); 41 } 42 return result; 43 } 44 45 std::optional<DIERef> 46 DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) { 47 std::optional<uint64_t> cu_offset = entry.getCUOffset(); 48 if (!cu_offset) 49 return std::nullopt; 50 51 DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset); 52 if (!cu) 53 return std::nullopt; 54 55 cu = &cu->GetNonSkeletonUnit(); 56 if (std::optional<uint64_t> die_offset = entry.getDIEUnitOffset()) 57 return DIERef(cu->GetSymbolFileDWARF().GetDwoNum(), 58 DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset); 59 60 return std::nullopt; 61 } 62 63 bool DebugNamesDWARFIndex::ProcessEntry( 64 const DebugNames::Entry &entry, 65 llvm::function_ref<bool(DWARFDIE die)> callback, llvm::StringRef name) { 66 std::optional<DIERef> ref = ToDIERef(entry); 67 if (!ref) 68 return true; 69 SymbolFileDWARF &dwarf = *llvm::cast<SymbolFileDWARF>( 70 m_module.GetSymbolFile()->GetBackingSymbolFile()); 71 DWARFDIE die = dwarf.GetDIE(*ref); 72 if (!die) 73 return true; 74 return callback(die); 75 } 76 77 void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error, 78 const DebugNames::NameIndex &ni, 79 llvm::StringRef name) { 80 // Ignore SentinelErrors, log everything else. 81 LLDB_LOG_ERROR( 82 GetLog(DWARFLog::Lookups), 83 handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}), 84 "Failed to parse index entries for index at {1:x}, name {2}: {0}", 85 ni.getUnitOffset(), name); 86 } 87 88 void DebugNamesDWARFIndex::GetGlobalVariables( 89 ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) { 90 for (const DebugNames::Entry &entry : 91 m_debug_names_up->equal_range(basename.GetStringRef())) { 92 if (entry.tag() != DW_TAG_variable) 93 continue; 94 95 if (!ProcessEntry(entry, callback, basename.GetStringRef())) 96 return; 97 } 98 99 m_fallback.GetGlobalVariables(basename, callback); 100 } 101 102 void DebugNamesDWARFIndex::GetGlobalVariables( 103 const RegularExpression ®ex, 104 llvm::function_ref<bool(DWARFDIE die)> callback) { 105 for (const DebugNames::NameIndex &ni: *m_debug_names_up) { 106 for (DebugNames::NameTableEntry nte: ni) { 107 if (!regex.Execute(nte.getString())) 108 continue; 109 110 uint64_t entry_offset = nte.getEntryOffset(); 111 llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); 112 for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { 113 if (entry_or->tag() != DW_TAG_variable) 114 continue; 115 116 if (!ProcessEntry(*entry_or, callback, 117 llvm::StringRef(nte.getString()))) 118 return; 119 } 120 MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); 121 } 122 } 123 124 m_fallback.GetGlobalVariables(regex, callback); 125 } 126 127 void DebugNamesDWARFIndex::GetGlobalVariables( 128 DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) { 129 lldbassert(!cu.GetSymbolFileDWARF().GetDwoNum()); 130 uint64_t cu_offset = cu.GetOffset(); 131 bool found_entry_for_cu = false; 132 for (const DebugNames::NameIndex &ni: *m_debug_names_up) { 133 for (DebugNames::NameTableEntry nte: ni) { 134 uint64_t entry_offset = nte.getEntryOffset(); 135 llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); 136 for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { 137 if (entry_or->tag() != DW_TAG_variable) 138 continue; 139 if (entry_or->getCUOffset() != cu_offset) 140 continue; 141 142 found_entry_for_cu = true; 143 if (!ProcessEntry(*entry_or, callback, 144 llvm::StringRef(nte.getString()))) 145 return; 146 } 147 MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); 148 } 149 } 150 // If no name index for that particular CU was found, fallback to 151 // creating the manual index. 152 if (!found_entry_for_cu) 153 m_fallback.GetGlobalVariables(cu, callback); 154 } 155 156 void DebugNamesDWARFIndex::GetCompleteObjCClass( 157 ConstString class_name, bool must_be_implementation, 158 llvm::function_ref<bool(DWARFDIE die)> callback) { 159 // Keep a list of incomplete types as fallback for when we don't find the 160 // complete type. 161 DIEArray incomplete_types; 162 163 for (const DebugNames::Entry &entry : 164 m_debug_names_up->equal_range(class_name.GetStringRef())) { 165 if (entry.tag() != DW_TAG_structure_type && 166 entry.tag() != DW_TAG_class_type) 167 continue; 168 169 std::optional<DIERef> ref = ToDIERef(entry); 170 if (!ref) 171 continue; 172 173 DWARFUnit *cu = m_debug_info.GetUnit(*ref); 174 if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) { 175 incomplete_types.push_back(*ref); 176 continue; 177 } 178 179 DWARFDIE die = m_debug_info.GetDIE(*ref); 180 if (!die) { 181 ReportInvalidDIERef(*ref, class_name.GetStringRef()); 182 continue; 183 } 184 185 if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) { 186 // If we find the complete version we're done. 187 callback(die); 188 return; 189 } 190 incomplete_types.push_back(*ref); 191 } 192 193 auto dierefcallback = DIERefCallback(callback, class_name.GetStringRef()); 194 for (DIERef ref : incomplete_types) 195 if (!dierefcallback(ref)) 196 return; 197 198 m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback); 199 } 200 201 void DebugNamesDWARFIndex::GetTypes( 202 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { 203 for (const DebugNames::Entry &entry : 204 m_debug_names_up->equal_range(name.GetStringRef())) { 205 if (isType(entry.tag())) { 206 if (!ProcessEntry(entry, callback, name.GetStringRef())) 207 return; 208 } 209 } 210 211 m_fallback.GetTypes(name, callback); 212 } 213 214 void DebugNamesDWARFIndex::GetTypes( 215 const DWARFDeclContext &context, 216 llvm::function_ref<bool(DWARFDIE die)> callback) { 217 auto name = context[0].name; 218 for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) { 219 if (entry.tag() == context[0].tag) { 220 if (!ProcessEntry(entry, callback, name)) 221 return; 222 } 223 } 224 225 m_fallback.GetTypes(context, callback); 226 } 227 228 void DebugNamesDWARFIndex::GetNamespaces( 229 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { 230 for (const DebugNames::Entry &entry : 231 m_debug_names_up->equal_range(name.GetStringRef())) { 232 if (entry.tag() == DW_TAG_namespace) { 233 if (!ProcessEntry(entry, callback, name.GetStringRef())) 234 return; 235 } 236 } 237 238 m_fallback.GetNamespaces(name, callback); 239 } 240 241 void DebugNamesDWARFIndex::GetFunctions( 242 const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, 243 const CompilerDeclContext &parent_decl_ctx, 244 llvm::function_ref<bool(DWARFDIE die)> callback) { 245 ConstString name = lookup_info.GetLookupName(); 246 std::set<DWARFDebugInfoEntry *> seen; 247 for (const DebugNames::Entry &entry : 248 m_debug_names_up->equal_range(name.GetStringRef())) { 249 Tag tag = entry.tag(); 250 if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) 251 continue; 252 253 if (std::optional<DIERef> ref = ToDIERef(entry)) { 254 if (!ProcessFunctionDIE(lookup_info, *ref, dwarf, parent_decl_ctx, 255 [&](DWARFDIE die) { 256 if (!seen.insert(die.GetDIE()).second) 257 return true; 258 return callback(die); 259 })) 260 return; 261 } 262 } 263 264 m_fallback.GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback); 265 } 266 267 void DebugNamesDWARFIndex::GetFunctions( 268 const RegularExpression ®ex, 269 llvm::function_ref<bool(DWARFDIE die)> callback) { 270 for (const DebugNames::NameIndex &ni: *m_debug_names_up) { 271 for (DebugNames::NameTableEntry nte: ni) { 272 if (!regex.Execute(nte.getString())) 273 continue; 274 275 uint64_t entry_offset = nte.getEntryOffset(); 276 llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset); 277 for (; entry_or; entry_or = ni.getEntry(&entry_offset)) { 278 Tag tag = entry_or->tag(); 279 if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) 280 continue; 281 282 if (!ProcessEntry(*entry_or, callback, 283 llvm::StringRef(nte.getString()))) 284 return; 285 } 286 MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); 287 } 288 } 289 290 m_fallback.GetFunctions(regex, callback); 291 } 292 293 void DebugNamesDWARFIndex::Dump(Stream &s) { 294 m_fallback.Dump(s); 295 296 std::string data; 297 llvm::raw_string_ostream os(data); 298 m_debug_names_up->dump(os); 299 s.PutCString(os.str()); 300 } 301