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