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