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