1 //===-- ManualDWARFIndex.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/ManualDWARFIndex.h" 10 #include "Plugins/Language/ObjC/ObjCLanguage.h" 11 #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" 12 #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h" 13 #include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h" 14 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" 15 #include "lldb/Core/Module.h" 16 #include "lldb/Core/Progress.h" 17 #include "lldb/Symbol/ObjectFile.h" 18 #include "lldb/Utility/Stream.h" 19 #include "lldb/Utility/Timer.h" 20 #include "llvm/Support/FormatVariadic.h" 21 #include "llvm/Support/ThreadPool.h" 22 23 using namespace lldb_private; 24 using namespace lldb; 25 26 void ManualDWARFIndex::Index() { 27 if (!m_dwarf) 28 return; 29 30 SymbolFileDWARF &main_dwarf = *m_dwarf; 31 m_dwarf = nullptr; 32 33 ElapsedTime elapsed(m_index_time); 34 LLDB_SCOPED_TIMERF("%p", static_cast<void *>(&main_dwarf)); 35 36 DWARFDebugInfo &main_info = main_dwarf.DebugInfo(); 37 SymbolFileDWARFDwo *dwp_dwarf = main_dwarf.GetDwpSymbolFile().get(); 38 DWARFDebugInfo *dwp_info = dwp_dwarf ? &dwp_dwarf->DebugInfo() : nullptr; 39 40 std::vector<DWARFUnit *> units_to_index; 41 units_to_index.reserve(main_info.GetNumUnits() + 42 (dwp_info ? dwp_info->GetNumUnits() : 0)); 43 44 // Process all units in the main file, as well as any type units in the dwp 45 // file. Type units in dwo files are handled when we reach the dwo file in 46 // IndexUnit. 47 for (size_t U = 0; U < main_info.GetNumUnits(); ++U) { 48 DWARFUnit *unit = main_info.GetUnitAtIndex(U); 49 if (unit && m_units_to_avoid.count(unit->GetOffset()) == 0) 50 units_to_index.push_back(unit); 51 } 52 if (dwp_info && dwp_info->ContainsTypeUnits()) { 53 for (size_t U = 0; U < dwp_info->GetNumUnits(); ++U) { 54 if (auto *tu = llvm::dyn_cast<DWARFTypeUnit>(dwp_info->GetUnitAtIndex(U))) 55 units_to_index.push_back(tu); 56 } 57 } 58 59 if (units_to_index.empty()) 60 return; 61 62 StreamString module_desc; 63 m_module.GetDescription(module_desc.AsRawOstream(), 64 lldb::eDescriptionLevelBrief); 65 66 // Include 2 passes per unit to index for extracting DIEs from the unit and 67 // indexing the unit, and then 8 extra entries for finalizing each index set. 68 const uint64_t total_progress = units_to_index.size() * 2 + 8; 69 Progress progress( 70 llvm::formatv("Manually indexing DWARF for {0}", module_desc.GetData()), 71 total_progress); 72 73 std::vector<IndexSet> sets(units_to_index.size()); 74 75 // Keep memory down by clearing DIEs for any units if indexing 76 // caused us to load the unit's DIEs. 77 std::vector<llvm::Optional<DWARFUnit::ScopedExtractDIEs>> clear_cu_dies( 78 units_to_index.size()); 79 auto parser_fn = [&](size_t cu_idx) { 80 IndexUnit(*units_to_index[cu_idx], dwp_dwarf, sets[cu_idx]); 81 progress.Increment(); 82 }; 83 84 auto extract_fn = [&](size_t cu_idx) { 85 clear_cu_dies[cu_idx] = units_to_index[cu_idx]->ExtractDIEsScoped(); 86 progress.Increment(); 87 }; 88 89 // Share one thread pool across operations to avoid the overhead of 90 // recreating the threads. 91 llvm::ThreadPool pool(llvm::optimal_concurrency(units_to_index.size())); 92 93 // Create a task runner that extracts dies for each DWARF unit in a 94 // separate thread. 95 // First figure out which units didn't have their DIEs already 96 // parsed and remember this. If no DIEs were parsed prior to this index 97 // function call, we are going to want to clear the CU dies after we are 98 // done indexing to make sure we don't pull in all DWARF dies, but we need 99 // to wait until all units have been indexed in case a DIE in one 100 // unit refers to another and the indexes accesses those DIEs. 101 for (size_t i = 0; i < units_to_index.size(); ++i) 102 pool.async(extract_fn, i); 103 pool.wait(); 104 105 // Now create a task runner that can index each DWARF unit in a 106 // separate thread so we can index quickly. 107 for (size_t i = 0; i < units_to_index.size(); ++i) 108 pool.async(parser_fn, i); 109 pool.wait(); 110 111 auto finalize_fn = [this, &sets, &progress](NameToDIE(IndexSet::*index)) { 112 NameToDIE &result = m_set.*index; 113 for (auto &set : sets) 114 result.Append(set.*index); 115 result.Finalize(); 116 progress.Increment(); 117 }; 118 119 pool.async(finalize_fn, &IndexSet::function_basenames); 120 pool.async(finalize_fn, &IndexSet::function_fullnames); 121 pool.async(finalize_fn, &IndexSet::function_methods); 122 pool.async(finalize_fn, &IndexSet::function_selectors); 123 pool.async(finalize_fn, &IndexSet::objc_class_selectors); 124 pool.async(finalize_fn, &IndexSet::globals); 125 pool.async(finalize_fn, &IndexSet::types); 126 pool.async(finalize_fn, &IndexSet::namespaces); 127 pool.wait(); 128 } 129 130 void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, SymbolFileDWARFDwo *dwp, 131 IndexSet &set) { 132 Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS); 133 134 if (log) { 135 m_module.LogMessage( 136 log, "ManualDWARFIndex::IndexUnit for unit at .debug_info[0x%8.8x]", 137 unit.GetOffset()); 138 } 139 140 const LanguageType cu_language = SymbolFileDWARF::GetLanguage(unit); 141 142 IndexUnitImpl(unit, cu_language, set); 143 144 if (SymbolFileDWARFDwo *dwo_symbol_file = unit.GetDwoSymbolFile()) { 145 // Type units in a dwp file are indexed separately, so we just need to 146 // process the split unit here. However, if the split unit is in a dwo file, 147 // then we need to process type units here. 148 if (dwo_symbol_file == dwp) { 149 IndexUnitImpl(unit.GetNonSkeletonUnit(), cu_language, set); 150 } else { 151 DWARFDebugInfo &dwo_info = dwo_symbol_file->DebugInfo(); 152 for (size_t i = 0; i < dwo_info.GetNumUnits(); ++i) 153 IndexUnitImpl(*dwo_info.GetUnitAtIndex(i), cu_language, set); 154 } 155 } 156 } 157 158 void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit, 159 const LanguageType cu_language, 160 IndexSet &set) { 161 for (const DWARFDebugInfoEntry &die : unit.dies()) { 162 const dw_tag_t tag = die.Tag(); 163 164 switch (tag) { 165 case DW_TAG_array_type: 166 case DW_TAG_base_type: 167 case DW_TAG_class_type: 168 case DW_TAG_constant: 169 case DW_TAG_enumeration_type: 170 case DW_TAG_inlined_subroutine: 171 case DW_TAG_namespace: 172 case DW_TAG_string_type: 173 case DW_TAG_structure_type: 174 case DW_TAG_subprogram: 175 case DW_TAG_subroutine_type: 176 case DW_TAG_typedef: 177 case DW_TAG_union_type: 178 case DW_TAG_unspecified_type: 179 case DW_TAG_variable: 180 break; 181 182 default: 183 continue; 184 } 185 186 DWARFAttributes attributes; 187 const char *name = nullptr; 188 const char *mangled_cstr = nullptr; 189 bool is_declaration = false; 190 // bool is_artificial = false; 191 bool has_address = false; 192 bool has_location_or_const_value = false; 193 bool is_global_or_static_variable = false; 194 195 DWARFFormValue specification_die_form; 196 const size_t num_attributes = die.GetAttributes(&unit, attributes); 197 if (num_attributes > 0) { 198 for (uint32_t i = 0; i < num_attributes; ++i) { 199 dw_attr_t attr = attributes.AttributeAtIndex(i); 200 DWARFFormValue form_value; 201 switch (attr) { 202 case DW_AT_name: 203 if (attributes.ExtractFormValueAtIndex(i, form_value)) 204 name = form_value.AsCString(); 205 break; 206 207 case DW_AT_declaration: 208 if (attributes.ExtractFormValueAtIndex(i, form_value)) 209 is_declaration = form_value.Unsigned() != 0; 210 break; 211 212 case DW_AT_MIPS_linkage_name: 213 case DW_AT_linkage_name: 214 if (attributes.ExtractFormValueAtIndex(i, form_value)) 215 mangled_cstr = form_value.AsCString(); 216 break; 217 218 case DW_AT_low_pc: 219 case DW_AT_high_pc: 220 case DW_AT_ranges: 221 has_address = true; 222 break; 223 224 case DW_AT_entry_pc: 225 has_address = true; 226 break; 227 228 case DW_AT_location: 229 case DW_AT_const_value: 230 has_location_or_const_value = true; 231 is_global_or_static_variable = die.IsGlobalOrStaticScopeVariable(); 232 233 break; 234 235 case DW_AT_specification: 236 if (attributes.ExtractFormValueAtIndex(i, form_value)) 237 specification_die_form = form_value; 238 break; 239 } 240 } 241 } 242 243 DIERef ref = *DWARFDIE(&unit, &die).GetDIERef(); 244 switch (tag) { 245 case DW_TAG_inlined_subroutine: 246 case DW_TAG_subprogram: 247 if (has_address) { 248 if (name) { 249 bool is_objc_method = false; 250 if (cu_language == eLanguageTypeObjC || 251 cu_language == eLanguageTypeObjC_plus_plus) { 252 ObjCLanguage::MethodName objc_method(name, true); 253 if (objc_method.IsValid(true)) { 254 is_objc_method = true; 255 ConstString class_name_with_category( 256 objc_method.GetClassNameWithCategory()); 257 ConstString objc_selector_name(objc_method.GetSelector()); 258 ConstString objc_fullname_no_category_name( 259 objc_method.GetFullNameWithoutCategory(true)); 260 ConstString class_name_no_category(objc_method.GetClassName()); 261 set.function_fullnames.Insert(ConstString(name), ref); 262 if (class_name_with_category) 263 set.objc_class_selectors.Insert(class_name_with_category, ref); 264 if (class_name_no_category && 265 class_name_no_category != class_name_with_category) 266 set.objc_class_selectors.Insert(class_name_no_category, ref); 267 if (objc_selector_name) 268 set.function_selectors.Insert(objc_selector_name, ref); 269 if (objc_fullname_no_category_name) 270 set.function_fullnames.Insert(objc_fullname_no_category_name, 271 ref); 272 } 273 } 274 // If we have a mangled name, then the DW_AT_name attribute is 275 // usually the method name without the class or any parameters 276 bool is_method = DWARFDIE(&unit, &die).IsMethod(); 277 278 if (is_method) 279 set.function_methods.Insert(ConstString(name), ref); 280 else 281 set.function_basenames.Insert(ConstString(name), ref); 282 283 if (!is_method && !mangled_cstr && !is_objc_method) 284 set.function_fullnames.Insert(ConstString(name), ref); 285 } 286 if (mangled_cstr) { 287 // Make sure our mangled name isn't the same string table entry as 288 // our name. If it starts with '_', then it is ok, else compare the 289 // string to make sure it isn't the same and we don't end up with 290 // duplicate entries 291 if (name && name != mangled_cstr && 292 ((mangled_cstr[0] == '_') || 293 (::strcmp(name, mangled_cstr) != 0))) { 294 set.function_fullnames.Insert(ConstString(mangled_cstr), ref); 295 } 296 } 297 } 298 break; 299 300 case DW_TAG_array_type: 301 case DW_TAG_base_type: 302 case DW_TAG_class_type: 303 case DW_TAG_constant: 304 case DW_TAG_enumeration_type: 305 case DW_TAG_string_type: 306 case DW_TAG_structure_type: 307 case DW_TAG_subroutine_type: 308 case DW_TAG_typedef: 309 case DW_TAG_union_type: 310 case DW_TAG_unspecified_type: 311 if (name && !is_declaration) 312 set.types.Insert(ConstString(name), ref); 313 if (mangled_cstr && !is_declaration) 314 set.types.Insert(ConstString(mangled_cstr), ref); 315 break; 316 317 case DW_TAG_namespace: 318 if (name) 319 set.namespaces.Insert(ConstString(name), ref); 320 break; 321 322 case DW_TAG_variable: 323 if (name && has_location_or_const_value && is_global_or_static_variable) { 324 set.globals.Insert(ConstString(name), ref); 325 // Be sure to include variables by their mangled and demangled names if 326 // they have any since a variable can have a basename "i", a mangled 327 // named "_ZN12_GLOBAL__N_11iE" and a demangled mangled name 328 // "(anonymous namespace)::i"... 329 330 // Make sure our mangled name isn't the same string table entry as our 331 // name. If it starts with '_', then it is ok, else compare the string 332 // to make sure it isn't the same and we don't end up with duplicate 333 // entries 334 if (mangled_cstr && name != mangled_cstr && 335 ((mangled_cstr[0] == '_') || (::strcmp(name, mangled_cstr) != 0))) { 336 set.globals.Insert(ConstString(mangled_cstr), ref); 337 } 338 } 339 break; 340 341 default: 342 continue; 343 } 344 } 345 } 346 347 void ManualDWARFIndex::GetGlobalVariables( 348 ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) { 349 Index(); 350 m_set.globals.Find(basename, 351 DIERefCallback(callback, basename.GetStringRef())); 352 } 353 354 void ManualDWARFIndex::GetGlobalVariables( 355 const RegularExpression ®ex, 356 llvm::function_ref<bool(DWARFDIE die)> callback) { 357 Index(); 358 m_set.globals.Find(regex, DIERefCallback(callback, regex.GetText())); 359 } 360 361 void ManualDWARFIndex::GetGlobalVariables( 362 DWARFUnit &unit, llvm::function_ref<bool(DWARFDIE die)> callback) { 363 lldbassert(!unit.GetSymbolFileDWARF().GetDwoNum()); 364 Index(); 365 m_set.globals.FindAllEntriesForUnit(unit, DIERefCallback(callback)); 366 } 367 368 void ManualDWARFIndex::GetObjCMethods( 369 ConstString class_name, llvm::function_ref<bool(DWARFDIE die)> callback) { 370 Index(); 371 m_set.objc_class_selectors.Find( 372 class_name, DIERefCallback(callback, class_name.GetStringRef())); 373 } 374 375 void ManualDWARFIndex::GetCompleteObjCClass( 376 ConstString class_name, bool must_be_implementation, 377 llvm::function_ref<bool(DWARFDIE die)> callback) { 378 Index(); 379 m_set.types.Find(class_name, 380 DIERefCallback(callback, class_name.GetStringRef())); 381 } 382 383 void ManualDWARFIndex::GetTypes( 384 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { 385 Index(); 386 m_set.types.Find(name, DIERefCallback(callback, name.GetStringRef())); 387 } 388 389 void ManualDWARFIndex::GetTypes( 390 const DWARFDeclContext &context, 391 llvm::function_ref<bool(DWARFDIE die)> callback) { 392 Index(); 393 auto name = context[0].name; 394 m_set.types.Find(ConstString(name), 395 DIERefCallback(callback, llvm::StringRef(name))); 396 } 397 398 void ManualDWARFIndex::GetNamespaces( 399 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { 400 Index(); 401 m_set.namespaces.Find(name, DIERefCallback(callback, name.GetStringRef())); 402 } 403 404 void ManualDWARFIndex::GetFunctions( 405 ConstString name, SymbolFileDWARF &dwarf, 406 const CompilerDeclContext &parent_decl_ctx, uint32_t name_type_mask, 407 llvm::function_ref<bool(DWARFDIE die)> callback) { 408 Index(); 409 410 if (name_type_mask & eFunctionNameTypeFull) { 411 if (!m_set.function_fullnames.Find( 412 name, DIERefCallback( 413 [&](DWARFDIE die) { 414 if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, 415 die)) 416 return true; 417 return callback(die); 418 }, 419 name.GetStringRef()))) 420 return; 421 } 422 if (name_type_mask & eFunctionNameTypeBase) { 423 if (!m_set.function_basenames.Find( 424 name, DIERefCallback( 425 [&](DWARFDIE die) { 426 if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, 427 die)) 428 return true; 429 return callback(die); 430 }, 431 name.GetStringRef()))) 432 return; 433 } 434 435 if (name_type_mask & eFunctionNameTypeMethod && !parent_decl_ctx.IsValid()) { 436 if (!m_set.function_methods.Find( 437 name, DIERefCallback(callback, name.GetStringRef()))) 438 return; 439 } 440 441 if (name_type_mask & eFunctionNameTypeSelector && 442 !parent_decl_ctx.IsValid()) { 443 if (!m_set.function_selectors.Find( 444 name, DIERefCallback(callback, name.GetStringRef()))) 445 return; 446 } 447 } 448 449 void ManualDWARFIndex::GetFunctions( 450 const RegularExpression ®ex, 451 llvm::function_ref<bool(DWARFDIE die)> callback) { 452 Index(); 453 454 if (!m_set.function_basenames.Find(regex, 455 DIERefCallback(callback, regex.GetText()))) 456 return; 457 if (!m_set.function_fullnames.Find(regex, 458 DIERefCallback(callback, regex.GetText()))) 459 return; 460 } 461 462 void ManualDWARFIndex::Dump(Stream &s) { 463 s.Format("Manual DWARF index for ({0}) '{1:F}':", 464 m_module.GetArchitecture().GetArchitectureName(), 465 m_module.GetObjectFile()->GetFileSpec()); 466 s.Printf("\nFunction basenames:\n"); 467 m_set.function_basenames.Dump(&s); 468 s.Printf("\nFunction fullnames:\n"); 469 m_set.function_fullnames.Dump(&s); 470 s.Printf("\nFunction methods:\n"); 471 m_set.function_methods.Dump(&s); 472 s.Printf("\nFunction selectors:\n"); 473 m_set.function_selectors.Dump(&s); 474 s.Printf("\nObjective-C class selectors:\n"); 475 m_set.objc_class_selectors.Dump(&s); 476 s.Printf("\nGlobals and statics:\n"); 477 m_set.globals.Dump(&s); 478 s.Printf("\nTypes:\n"); 479 m_set.types.Dump(&s); 480 s.Printf("\nNamespaces:\n"); 481 m_set.namespaces.Dump(&s); 482 } 483