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