| 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/DataFileCache.h" |
| 16 | #include "lldb/Core/Debugger.h" |
| 17 | #include "lldb/Core/Module.h" |
| 18 | #include "lldb/Core/Progress.h" |
| 19 | #include "lldb/Symbol/ObjectFile.h" |
| 20 | #include "lldb/Utility/DataEncoder.h" |
| 21 | #include "lldb/Utility/DataExtractor.h" |
| 22 | #include "lldb/Utility/Stream.h" |
| 23 | #include "lldb/Utility/Timer.h" |
| 24 | #include "llvm/Support/FormatVariadic.h" |
| 25 | #include "llvm/Support/ThreadPool.h" |
| 26 | #include <atomic> |
| 27 | #include <optional> |
| 28 | |
| 29 | using namespace lldb_private; |
| 30 | using namespace lldb; |
| 31 | using namespace lldb_private::dwarf; |
| 32 | using namespace lldb_private::plugin::dwarf; |
| 33 | |
| 34 | void ManualDWARFIndex::Index() { |
| 35 | if (m_indexed) |
| 36 | return; |
| 37 | m_indexed = true; |
| 38 | |
| 39 | ElapsedTime elapsed(m_index_time); |
| 40 | LLDB_SCOPED_TIMERF("%p" , static_cast<void *>(m_dwarf)); |
| 41 | if (LoadFromCache()) { |
| 42 | m_dwarf->SetDebugInfoIndexWasLoadedFromCache(); |
| 43 | return; |
| 44 | } |
| 45 | |
| 46 | DWARFDebugInfo &main_info = m_dwarf->DebugInfo(); |
| 47 | SymbolFileDWARFDwo *dwp_dwarf = m_dwarf->GetDwpSymbolFile().get(); |
| 48 | DWARFDebugInfo *dwp_info = dwp_dwarf ? &dwp_dwarf->DebugInfo() : nullptr; |
| 49 | |
| 50 | std::vector<DWARFUnit *> units_to_index; |
| 51 | units_to_index.reserve(n: main_info.GetNumUnits() + |
| 52 | (dwp_info ? dwp_info->GetNumUnits() : 0)); |
| 53 | |
| 54 | // Process all units in the main file, as well as any type units in the dwp |
| 55 | // file. Type units in dwo files are handled when we reach the dwo file in |
| 56 | // IndexUnit. |
| 57 | for (size_t U = 0; U < main_info.GetNumUnits(); ++U) { |
| 58 | DWARFUnit *unit = main_info.GetUnitAtIndex(idx: U); |
| 59 | if (unit && m_units_to_avoid.count(V: unit->GetOffset()) == 0) |
| 60 | units_to_index.push_back(x: unit); |
| 61 | } |
| 62 | if (dwp_info && dwp_info->ContainsTypeUnits()) { |
| 63 | for (size_t U = 0; U < dwp_info->GetNumUnits(); ++U) { |
| 64 | if (auto *tu = |
| 65 | llvm::dyn_cast<DWARFTypeUnit>(Val: dwp_info->GetUnitAtIndex(idx: U))) { |
| 66 | if (!m_type_sigs_to_avoid.contains(V: tu->GetTypeHash())) |
| 67 | units_to_index.push_back(x: tu); |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | if (units_to_index.empty()) |
| 73 | return; |
| 74 | |
| 75 | StreamString module_desc; |
| 76 | m_module.GetDescription(s&: module_desc.AsRawOstream(), |
| 77 | level: lldb::eDescriptionLevelBrief); |
| 78 | |
| 79 | // Include 2 passes per unit to index for extracting DIEs from the unit and |
| 80 | // indexing the unit, and then extra entries for finalizing each index in the |
| 81 | // set. |
| 82 | const auto indices = IndexSet<NameToDIE>::Indices(); |
| 83 | const uint64_t total_progress = units_to_index.size() * 2 + indices.size(); |
| 84 | Progress progress("Manually indexing DWARF" , module_desc.GetData(), |
| 85 | total_progress, /*debugger=*/nullptr, |
| 86 | Progress::kDefaultHighFrequencyReportTime); |
| 87 | |
| 88 | // Share one thread pool across operations to avoid the overhead of |
| 89 | // recreating the threads. |
| 90 | llvm::ThreadPoolTaskGroup task_group(Debugger::GetThreadPool()); |
| 91 | const size_t num_threads = Debugger::GetThreadPool().getMaxConcurrency(); |
| 92 | |
| 93 | // Run a function for each compile unit in parallel using as many threads as |
| 94 | // are available. This is significantly faster than submiting a new task for |
| 95 | // each unit. |
| 96 | auto for_each_unit = [&](auto &&fn) { |
| 97 | std::atomic<size_t> next_cu_idx = 0; |
| 98 | auto wrapper = [&fn, &next_cu_idx, &units_to_index, |
| 99 | &progress](size_t worker_id) { |
| 100 | size_t cu_idx; |
| 101 | while ((cu_idx = next_cu_idx.fetch_add(i: 1, m: std::memory_order_relaxed)) < |
| 102 | units_to_index.size()) { |
| 103 | fn(worker_id, cu_idx, units_to_index[cu_idx]); |
| 104 | progress.Increment(); |
| 105 | } |
| 106 | }; |
| 107 | |
| 108 | for (size_t i = 0; i < num_threads; ++i) |
| 109 | task_group.async(wrapper, i); |
| 110 | |
| 111 | task_group.wait(); |
| 112 | }; |
| 113 | |
| 114 | // Extract dies for all DWARFs unit in parallel. Figure out which units |
| 115 | // didn't have their DIEs already parsed and remember this. If no DIEs were |
| 116 | // parsed prior to this index function call, we are going to want to clear the |
| 117 | // CU dies after we are done indexing to make sure we don't pull in all DWARF |
| 118 | // dies, but we need to wait until all units have been indexed in case a DIE |
| 119 | // in one unit refers to another and the indexes accesses those DIEs. |
| 120 | std::vector<std::optional<DWARFUnit::ScopedExtractDIEs>> clear_cu_dies( |
| 121 | units_to_index.size()); |
| 122 | for_each_unit([&clear_cu_dies](size_t, size_t idx, DWARFUnit *unit) { |
| 123 | clear_cu_dies[idx] = unit->ExtractDIEsScoped(); |
| 124 | }); |
| 125 | |
| 126 | // Now index all DWARF unit in parallel. |
| 127 | std::vector<IndexSet<NameToDIE>> sets(num_threads); |
| 128 | for_each_unit( |
| 129 | [this, dwp_dwarf, &sets](size_t worker_id, size_t, DWARFUnit *unit) { |
| 130 | IndexUnit(unit&: *unit, dwp: dwp_dwarf, set&: sets[worker_id]); |
| 131 | }); |
| 132 | |
| 133 | // Merge partial indexes into a single index. Process each index in a set in |
| 134 | // parallel. |
| 135 | for (NameToDIE IndexSet<NameToDIE>::*index : indices) { |
| 136 | task_group.async(F: [this, &sets, index, &progress]() { |
| 137 | NameToDIE &result = m_set.*index; |
| 138 | for (auto &set : sets) |
| 139 | result.Append(other: set.*index); |
| 140 | result.Finalize(); |
| 141 | progress.Increment(); |
| 142 | }); |
| 143 | } |
| 144 | task_group.wait(); |
| 145 | |
| 146 | SaveToCache(); |
| 147 | } |
| 148 | |
| 149 | void ManualDWARFIndex::IndexUnit(DWARFUnit &unit, SymbolFileDWARFDwo *dwp, |
| 150 | IndexSet<NameToDIE> &set) { |
| 151 | Log *log = GetLog(mask: DWARFLog::Lookups); |
| 152 | |
| 153 | if (log) { |
| 154 | m_module.LogMessage( |
| 155 | log, format: "ManualDWARFIndex::IndexUnit for unit at .debug_info[{0:x16}]" , |
| 156 | args: unit.GetOffset()); |
| 157 | } |
| 158 | |
| 159 | const LanguageType cu_language = SymbolFileDWARF::GetLanguage(unit); |
| 160 | |
| 161 | // First check if the unit has a DWO ID. If it does then we only want to index |
| 162 | // the .dwo file or nothing at all. If we have a compile unit where we can't |
| 163 | // locate the .dwo/.dwp file we don't want to index anything from the skeleton |
| 164 | // compile unit because it is usally has no children unless |
| 165 | // -fsplit-dwarf-inlining was used at compile time. This option will add a |
| 166 | // copy of all DW_TAG_subprogram and any contained DW_TAG_inline_subroutine |
| 167 | // DIEs so that symbolication will still work in the absence of the .dwo/.dwp |
| 168 | // file, but the functions have no return types and all arguments and locals |
| 169 | // have been removed. So we don't want to index any of these hacked up |
| 170 | // function types. Types can still exist in the skeleton compile unit DWARF |
| 171 | // though as some functions have template parameter types and other things |
| 172 | // that cause extra copies of types to be included, but we should find these |
| 173 | // types in the .dwo file only as methods could have return types removed and |
| 174 | // we don't have to index incomplete types from the skeleton compile unit. |
| 175 | if (unit.GetDWOId()) { |
| 176 | // Index the .dwo or dwp instead of the skeleton unit. |
| 177 | if (SymbolFileDWARFDwo *dwo_symbol_file = unit.GetDwoSymbolFile()) { |
| 178 | // Type units in a dwp file are indexed separately, so we just need to |
| 179 | // process the split unit here. However, if the split unit is in a dwo |
| 180 | // file, then we need to process type units here. |
| 181 | if (dwo_symbol_file == dwp) { |
| 182 | IndexUnitImpl(unit&: unit.GetNonSkeletonUnit(), cu_language, set); |
| 183 | } else { |
| 184 | DWARFDebugInfo &dwo_info = dwo_symbol_file->DebugInfo(); |
| 185 | for (size_t i = 0; i < dwo_info.GetNumUnits(); ++i) |
| 186 | IndexUnitImpl(unit&: *dwo_info.GetUnitAtIndex(idx: i), cu_language, set); |
| 187 | } |
| 188 | return; |
| 189 | } |
| 190 | // This was a DWARF5 skeleton CU and the .dwo file couldn't be located. |
| 191 | if (unit.GetVersion() >= 5 && unit.IsSkeletonUnit()) |
| 192 | return; |
| 193 | |
| 194 | // Either this is a DWARF 4 + fission CU with the .dwo file |
| 195 | // missing, or it's a -gmodules pch or pcm. Try to detect the |
| 196 | // latter by checking whether the first DIE is a DW_TAG_module. |
| 197 | // If it's a pch/pcm, continue indexing it. |
| 198 | if (unit.GetDIE(die_offset: unit.GetFirstDIEOffset()).GetFirstChild().Tag() != |
| 199 | llvm::dwarf::DW_TAG_module) |
| 200 | return; |
| 201 | } |
| 202 | // We have a normal compile unit which we want to index. |
| 203 | IndexUnitImpl(unit, cu_language, set); |
| 204 | } |
| 205 | |
| 206 | void ManualDWARFIndex::IndexUnitImpl(DWARFUnit &unit, |
| 207 | const LanguageType cu_language, |
| 208 | IndexSet<NameToDIE> &set) { |
| 209 | for (const DWARFDebugInfoEntry &die : unit.dies()) { |
| 210 | const dw_tag_t tag = die.Tag(); |
| 211 | |
| 212 | switch (tag) { |
| 213 | case DW_TAG_array_type: |
| 214 | case DW_TAG_base_type: |
| 215 | case DW_TAG_class_type: |
| 216 | case DW_TAG_constant: |
| 217 | case DW_TAG_enumeration_type: |
| 218 | case DW_TAG_inlined_subroutine: |
| 219 | case DW_TAG_namespace: |
| 220 | case DW_TAG_imported_declaration: |
| 221 | case DW_TAG_string_type: |
| 222 | case DW_TAG_structure_type: |
| 223 | case DW_TAG_subprogram: |
| 224 | case DW_TAG_subroutine_type: |
| 225 | case DW_TAG_typedef: |
| 226 | case DW_TAG_union_type: |
| 227 | case DW_TAG_unspecified_type: |
| 228 | case DW_TAG_variable: |
| 229 | break; |
| 230 | |
| 231 | case DW_TAG_member: |
| 232 | // Only in DWARF 4 and earlier `static const` members of a struct, a class |
| 233 | // or a union have an entry tag `DW_TAG_member` |
| 234 | if (unit.GetVersion() >= 5) |
| 235 | continue; |
| 236 | break; |
| 237 | |
| 238 | default: |
| 239 | continue; |
| 240 | } |
| 241 | |
| 242 | const char *name = nullptr; |
| 243 | const char *mangled_cstr = nullptr; |
| 244 | bool is_declaration = false; |
| 245 | bool has_address = false; |
| 246 | bool has_location_or_const_value = false; |
| 247 | bool is_global_or_static_variable = false; |
| 248 | |
| 249 | DWARFFormValue specification_die_form; |
| 250 | DWARFAttributes attributes = die.GetAttributes(cu: &unit); |
| 251 | for (size_t i = 0; i < attributes.Size(); ++i) { |
| 252 | dw_attr_t attr = attributes.AttributeAtIndex(i); |
| 253 | DWARFFormValue form_value; |
| 254 | switch (attr) { |
| 255 | default: |
| 256 | break; |
| 257 | case DW_AT_name: |
| 258 | if (attributes.ExtractFormValueAtIndex(i, form_value)) |
| 259 | name = form_value.AsCString(); |
| 260 | break; |
| 261 | |
| 262 | case DW_AT_declaration: |
| 263 | if (attributes.ExtractFormValueAtIndex(i, form_value)) |
| 264 | is_declaration = form_value.Unsigned() != 0; |
| 265 | break; |
| 266 | |
| 267 | case DW_AT_MIPS_linkage_name: |
| 268 | case DW_AT_linkage_name: |
| 269 | if (attributes.ExtractFormValueAtIndex(i, form_value)) |
| 270 | mangled_cstr = form_value.AsCString(); |
| 271 | break; |
| 272 | |
| 273 | case DW_AT_low_pc: |
| 274 | case DW_AT_high_pc: |
| 275 | case DW_AT_ranges: |
| 276 | has_address = true; |
| 277 | break; |
| 278 | |
| 279 | case DW_AT_entry_pc: |
| 280 | has_address = true; |
| 281 | break; |
| 282 | |
| 283 | case DW_AT_location: |
| 284 | case DW_AT_const_value: |
| 285 | has_location_or_const_value = true; |
| 286 | is_global_or_static_variable = die.IsGlobalOrStaticScopeVariable(); |
| 287 | |
| 288 | break; |
| 289 | |
| 290 | case DW_AT_specification: |
| 291 | if (attributes.ExtractFormValueAtIndex(i, form_value)) |
| 292 | specification_die_form = form_value; |
| 293 | break; |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | DIERef ref = *DWARFDIE(&unit, &die).GetDIERef(); |
| 298 | switch (tag) { |
| 299 | case DW_TAG_inlined_subroutine: |
| 300 | case DW_TAG_subprogram: |
| 301 | if (has_address) { |
| 302 | if (name) { |
| 303 | bool is_objc_method = false; |
| 304 | if (cu_language == eLanguageTypeObjC || |
| 305 | cu_language == eLanguageTypeObjC_plus_plus) { |
| 306 | std::optional<const ObjCLanguage::ObjCMethodName> objc_method = |
| 307 | ObjCLanguage::ObjCMethodName::Create(name, strict: true); |
| 308 | if (objc_method) { |
| 309 | is_objc_method = true; |
| 310 | ConstString class_name_with_category( |
| 311 | objc_method->GetClassNameWithCategory()); |
| 312 | ConstString objc_selector_name(objc_method->GetSelector()); |
| 313 | ConstString objc_fullname_no_category_name( |
| 314 | objc_method->GetFullNameWithoutCategory().c_str()); |
| 315 | ConstString class_name_no_category(objc_method->GetClassName()); |
| 316 | set.function_fullnames.Insert(name: ConstString(name), die_ref: ref); |
| 317 | if (class_name_with_category) |
| 318 | set.objc_class_selectors.Insert(name: class_name_with_category, die_ref: ref); |
| 319 | if (class_name_no_category && |
| 320 | class_name_no_category != class_name_with_category) |
| 321 | set.objc_class_selectors.Insert(name: class_name_no_category, die_ref: ref); |
| 322 | if (objc_selector_name) |
| 323 | set.function_selectors.Insert(name: objc_selector_name, die_ref: ref); |
| 324 | if (objc_fullname_no_category_name) |
| 325 | set.function_fullnames.Insert(name: objc_fullname_no_category_name, |
| 326 | die_ref: ref); |
| 327 | } |
| 328 | } |
| 329 | // If we have a mangled name, then the DW_AT_name attribute is |
| 330 | // usually the method name without the class or any parameters |
| 331 | bool is_method = DWARFDIE(&unit, &die).IsMethod(); |
| 332 | |
| 333 | if (is_method) |
| 334 | set.function_methods.Insert(name: ConstString(name), die_ref: ref); |
| 335 | else |
| 336 | set.function_basenames.Insert(name: ConstString(name), die_ref: ref); |
| 337 | |
| 338 | if (!is_method && !mangled_cstr && !is_objc_method) |
| 339 | set.function_fullnames.Insert(name: ConstString(name), die_ref: ref); |
| 340 | } |
| 341 | if (mangled_cstr) { |
| 342 | // Make sure our mangled name isn't the same string table entry as |
| 343 | // our name. If it starts with '_', then it is ok, else compare the |
| 344 | // string to make sure it isn't the same and we don't end up with |
| 345 | // duplicate entries |
| 346 | if (name && name != mangled_cstr && |
| 347 | ((mangled_cstr[0] == '_') || |
| 348 | (::strcmp(s1: name, s2: mangled_cstr) != 0))) { |
| 349 | set.function_fullnames.Insert(name: ConstString(mangled_cstr), die_ref: ref); |
| 350 | } |
| 351 | } |
| 352 | } |
| 353 | break; |
| 354 | |
| 355 | case DW_TAG_array_type: |
| 356 | case DW_TAG_base_type: |
| 357 | case DW_TAG_class_type: |
| 358 | case DW_TAG_constant: |
| 359 | case DW_TAG_enumeration_type: |
| 360 | case DW_TAG_string_type: |
| 361 | case DW_TAG_structure_type: |
| 362 | case DW_TAG_subroutine_type: |
| 363 | case DW_TAG_typedef: |
| 364 | case DW_TAG_union_type: |
| 365 | case DW_TAG_unspecified_type: |
| 366 | if (name && !is_declaration) |
| 367 | set.types.Insert(name: ConstString(name), die_ref: ref); |
| 368 | if (mangled_cstr && !is_declaration) |
| 369 | set.types.Insert(name: ConstString(mangled_cstr), die_ref: ref); |
| 370 | break; |
| 371 | |
| 372 | case DW_TAG_namespace: |
| 373 | case DW_TAG_imported_declaration: |
| 374 | if (name) |
| 375 | set.namespaces.Insert(name: ConstString(name), die_ref: ref); |
| 376 | break; |
| 377 | |
| 378 | case DW_TAG_member: { |
| 379 | // In DWARF 4 and earlier `static const` members of a struct, a class or a |
| 380 | // union have an entry tag `DW_TAG_member`, and are also tagged as |
| 381 | // `DW_AT_declaration`, but otherwise follow the same rules as |
| 382 | // `DW_TAG_variable`. |
| 383 | bool parent_is_class_type = false; |
| 384 | if (auto parent = die.GetParent()) |
| 385 | parent_is_class_type = DWARFDIE(&unit, parent).IsStructUnionOrClass(); |
| 386 | if (!parent_is_class_type || !is_declaration) |
| 387 | break; |
| 388 | [[fallthrough]]; |
| 389 | } |
| 390 | case DW_TAG_variable: |
| 391 | if (name && has_location_or_const_value && is_global_or_static_variable) { |
| 392 | set.globals.Insert(name: ConstString(name), die_ref: ref); |
| 393 | // Be sure to include variables by their mangled and demangled names if |
| 394 | // they have any since a variable can have a basename "i", a mangled |
| 395 | // named "_ZN12_GLOBAL__N_11iE" and a demangled mangled name |
| 396 | // "(anonymous namespace)::i"... |
| 397 | |
| 398 | // Make sure our mangled name isn't the same string table entry as our |
| 399 | // name. If it starts with '_', then it is ok, else compare the string |
| 400 | // to make sure it isn't the same and we don't end up with duplicate |
| 401 | // entries |
| 402 | if (mangled_cstr && name != mangled_cstr && |
| 403 | ((mangled_cstr[0] == '_') || (::strcmp(s1: name, s2: mangled_cstr) != 0))) { |
| 404 | set.globals.Insert(name: ConstString(mangled_cstr), die_ref: ref); |
| 405 | } |
| 406 | } |
| 407 | break; |
| 408 | |
| 409 | default: |
| 410 | continue; |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | void ManualDWARFIndex::GetGlobalVariables( |
| 416 | ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 417 | Index(); |
| 418 | m_set.globals.Find(name: basename, |
| 419 | callback: DIERefCallback(callback, name: basename.GetStringRef())); |
| 420 | } |
| 421 | |
| 422 | void ManualDWARFIndex::GetGlobalVariables( |
| 423 | const RegularExpression ®ex, |
| 424 | llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 425 | Index(); |
| 426 | m_set.globals.Find(regex, callback: DIERefCallback(callback, name: regex.GetText())); |
| 427 | } |
| 428 | |
| 429 | void ManualDWARFIndex::GetGlobalVariables( |
| 430 | DWARFUnit &unit, llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 431 | Index(); |
| 432 | m_set.globals.FindAllEntriesForUnit(unit, callback: DIERefCallback(callback)); |
| 433 | } |
| 434 | |
| 435 | void ManualDWARFIndex::GetObjCMethods( |
| 436 | ConstString class_name, llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 437 | Index(); |
| 438 | m_set.objc_class_selectors.Find( |
| 439 | name: class_name, callback: DIERefCallback(callback, name: class_name.GetStringRef())); |
| 440 | } |
| 441 | |
| 442 | void ManualDWARFIndex::GetCompleteObjCClass( |
| 443 | ConstString class_name, bool must_be_implementation, |
| 444 | llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 445 | Index(); |
| 446 | m_set.types.Find(name: class_name, |
| 447 | callback: DIERefCallback(callback, name: class_name.GetStringRef())); |
| 448 | } |
| 449 | |
| 450 | void ManualDWARFIndex::GetTypes( |
| 451 | ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 452 | Index(); |
| 453 | m_set.types.Find(name, callback: DIERefCallback(callback, name: name.GetStringRef())); |
| 454 | } |
| 455 | |
| 456 | void ManualDWARFIndex::GetTypes( |
| 457 | const DWARFDeclContext &context, |
| 458 | llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 459 | Index(); |
| 460 | auto name = context[0].name; |
| 461 | m_set.types.Find(name: ConstString(name), |
| 462 | callback: DIERefCallback(callback, name: llvm::StringRef(name))); |
| 463 | } |
| 464 | |
| 465 | void ManualDWARFIndex::GetNamespaces( |
| 466 | ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 467 | Index(); |
| 468 | m_set.namespaces.Find(name, callback: DIERefCallback(callback, name: name.GetStringRef())); |
| 469 | } |
| 470 | |
| 471 | void ManualDWARFIndex::GetFunctions( |
| 472 | const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, |
| 473 | const CompilerDeclContext &parent_decl_ctx, |
| 474 | llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 475 | Index(); |
| 476 | ConstString name = lookup_info.GetLookupName(); |
| 477 | FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); |
| 478 | |
| 479 | if (name_type_mask & eFunctionNameTypeFull) { |
| 480 | if (!m_set.function_fullnames.Find( |
| 481 | name, callback: DIERefCallback( |
| 482 | callback: [&](DWARFDIE die) { |
| 483 | if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, |
| 484 | die)) |
| 485 | return true; |
| 486 | return callback(die); |
| 487 | }, |
| 488 | name: name.GetStringRef()))) |
| 489 | return; |
| 490 | } |
| 491 | if (name_type_mask & eFunctionNameTypeBase) { |
| 492 | if (!m_set.function_basenames.Find( |
| 493 | name, callback: DIERefCallback( |
| 494 | callback: [&](DWARFDIE die) { |
| 495 | if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, |
| 496 | die)) |
| 497 | return true; |
| 498 | return callback(die); |
| 499 | }, |
| 500 | name: name.GetStringRef()))) |
| 501 | return; |
| 502 | } |
| 503 | |
| 504 | if (name_type_mask & eFunctionNameTypeMethod && !parent_decl_ctx.IsValid()) { |
| 505 | if (!m_set.function_methods.Find( |
| 506 | name, callback: DIERefCallback(callback, name: name.GetStringRef()))) |
| 507 | return; |
| 508 | } |
| 509 | |
| 510 | if (name_type_mask & eFunctionNameTypeSelector && |
| 511 | !parent_decl_ctx.IsValid()) { |
| 512 | if (!m_set.function_selectors.Find( |
| 513 | name, callback: DIERefCallback(callback, name: name.GetStringRef()))) |
| 514 | return; |
| 515 | } |
| 516 | } |
| 517 | |
| 518 | void ManualDWARFIndex::GetFunctions( |
| 519 | const RegularExpression ®ex, |
| 520 | llvm::function_ref<bool(DWARFDIE die)> callback) { |
| 521 | Index(); |
| 522 | |
| 523 | if (!m_set.function_basenames.Find(regex, |
| 524 | callback: DIERefCallback(callback, name: regex.GetText()))) |
| 525 | return; |
| 526 | if (!m_set.function_fullnames.Find(regex, |
| 527 | callback: DIERefCallback(callback, name: regex.GetText()))) |
| 528 | return; |
| 529 | } |
| 530 | |
| 531 | void ManualDWARFIndex::Dump(Stream &s) { |
| 532 | s.Format(format: "Manual DWARF index for ({0}) '{1:F}':" , |
| 533 | args: m_module.GetArchitecture().GetArchitectureName(), |
| 534 | args&: m_module.GetObjectFile()->GetFileSpec()); |
| 535 | s.Printf(format: "\nFunction basenames:\n" ); |
| 536 | m_set.function_basenames.Dump(s: &s); |
| 537 | s.Printf(format: "\nFunction fullnames:\n" ); |
| 538 | m_set.function_fullnames.Dump(s: &s); |
| 539 | s.Printf(format: "\nFunction methods:\n" ); |
| 540 | m_set.function_methods.Dump(s: &s); |
| 541 | s.Printf(format: "\nFunction selectors:\n" ); |
| 542 | m_set.function_selectors.Dump(s: &s); |
| 543 | s.Printf(format: "\nObjective-C class selectors:\n" ); |
| 544 | m_set.objc_class_selectors.Dump(s: &s); |
| 545 | s.Printf(format: "\nGlobals and statics:\n" ); |
| 546 | m_set.globals.Dump(s: &s); |
| 547 | s.Printf(format: "\nTypes:\n" ); |
| 548 | m_set.types.Dump(s: &s); |
| 549 | s.Printf(format: "\nNamespaces:\n" ); |
| 550 | m_set.namespaces.Dump(s: &s); |
| 551 | } |
| 552 | |
| 553 | bool ManualDWARFIndex::(const DataExtractor &data, |
| 554 | lldb::offset_t *offset_ptr, |
| 555 | bool &signature_mismatch) { |
| 556 | signature_mismatch = false; |
| 557 | CacheSignature signature; |
| 558 | if (!signature.Decode(data, offset_ptr)) |
| 559 | return false; |
| 560 | if (CacheSignature(m_dwarf->GetObjectFile()) != signature) { |
| 561 | signature_mismatch = true; |
| 562 | return false; |
| 563 | } |
| 564 | std::optional<IndexSet<NameToDIE>> set = DecodeIndexSet(data, offset_ptr); |
| 565 | if (!set) |
| 566 | return false; |
| 567 | m_set = std::move(*set); |
| 568 | return true; |
| 569 | } |
| 570 | |
| 571 | bool ManualDWARFIndex::Encode(DataEncoder &encoder) const { |
| 572 | CacheSignature signature(m_dwarf->GetObjectFile()); |
| 573 | if (!signature.Encode(encoder)) |
| 574 | return false; |
| 575 | EncodeIndexSet(set: m_set, encoder); |
| 576 | return true; |
| 577 | } |
| 578 | |
| 579 | bool ManualDWARFIndex::IsPartial() const { |
| 580 | // If we have units or type units to skip, then this index is partial. |
| 581 | return !m_units_to_avoid.empty() || !m_type_sigs_to_avoid.empty(); |
| 582 | } |
| 583 | |
| 584 | std::string ManualDWARFIndex::GetCacheKey() { |
| 585 | std::string key; |
| 586 | llvm::raw_string_ostream strm(key); |
| 587 | // DWARF Index can come from different object files for the same module. A |
| 588 | // module can have one object file as the main executable and might have |
| 589 | // another object file in a separate symbol file, or we might have a .dwo file |
| 590 | // that claims its module is the main executable. |
| 591 | |
| 592 | // This class can be used to index all of the DWARF, or part of the DWARF |
| 593 | // when there is a .debug_names index where some compile or type units were |
| 594 | // built without .debug_names. So we need to know when we have a full manual |
| 595 | // DWARF index or a partial manual DWARF index and save them to different |
| 596 | // cache files. Before this fix we might end up debugging a binary with |
| 597 | // .debug_names where some of the compile or type units weren't indexed, and |
| 598 | // find an issue with the .debug_names tables (bugs or being incomplete), and |
| 599 | // then we disable loading the .debug_names by setting a setting in LLDB by |
| 600 | // running "settings set plugin.symbol-file.dwarf.ignore-file-indexes 0" in |
| 601 | // another LLDB instance. The problem arose when there was an index cache from |
| 602 | // a previous run where .debug_names was enabled and it had saved a cache file |
| 603 | // that only covered the missing compile and type units from the .debug_names, |
| 604 | // and with the setting that disables the loading of the cache files we would |
| 605 | // load partial cache index cache. So we need to pick a unique cache suffix |
| 606 | // name that indicates if the cache is partial or full to avoid this problem. |
| 607 | llvm::StringRef dwarf_index_suffix(IsPartial() ? "partial-" : "full-" ); |
| 608 | ObjectFile *objfile = m_dwarf->GetObjectFile(); |
| 609 | strm << objfile->GetModule()->GetCacheKey() << "-dwarf-index-" |
| 610 | << dwarf_index_suffix << llvm::format_hex(N: objfile->GetCacheHash(), Width: 10); |
| 611 | return key; |
| 612 | } |
| 613 | |
| 614 | bool ManualDWARFIndex::LoadFromCache() { |
| 615 | DataFileCache *cache = Module::GetIndexCache(); |
| 616 | if (!cache) |
| 617 | return false; |
| 618 | ObjectFile *objfile = m_dwarf->GetObjectFile(); |
| 619 | if (!objfile) |
| 620 | return false; |
| 621 | std::unique_ptr<llvm::MemoryBuffer> mem_buffer_up = |
| 622 | cache->GetCachedData(key: GetCacheKey()); |
| 623 | if (!mem_buffer_up) |
| 624 | return false; |
| 625 | DataExtractor data(mem_buffer_up->getBufferStart(), |
| 626 | mem_buffer_up->getBufferSize(), |
| 627 | endian::InlHostByteOrder(), |
| 628 | objfile->GetAddressByteSize()); |
| 629 | bool signature_mismatch = false; |
| 630 | lldb::offset_t offset = 0; |
| 631 | const bool result = Decode(data, offset_ptr: &offset, signature_mismatch); |
| 632 | if (signature_mismatch) |
| 633 | cache->RemoveCacheFile(key: GetCacheKey()); |
| 634 | return result; |
| 635 | } |
| 636 | |
| 637 | void ManualDWARFIndex::SaveToCache() { |
| 638 | DataFileCache *cache = Module::GetIndexCache(); |
| 639 | if (!cache) |
| 640 | return; // Caching is not enabled. |
| 641 | ObjectFile *objfile = m_dwarf->GetObjectFile(); |
| 642 | if (!objfile) |
| 643 | return; |
| 644 | DataEncoder file(endian::InlHostByteOrder(), objfile->GetAddressByteSize()); |
| 645 | // Encode will return false if the object file doesn't have anything to make |
| 646 | // a signature from. |
| 647 | if (Encode(encoder&: file)) { |
| 648 | if (cache->SetCachedData(key: GetCacheKey(), data: file.GetData())) |
| 649 | m_dwarf->SetDebugInfoIndexWasSavedToCache(); |
| 650 | } |
| 651 | } |
| 652 | |