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