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 | |