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
29using namespace lldb_private;
30using namespace lldb;
31using namespace lldb_private::dwarf;
32using namespace lldb_private::plugin::dwarf;
33
34void 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
149void 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
206void 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
415void 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
422void ManualDWARFIndex::GetGlobalVariables(
423 const RegularExpression &regex,
424 llvm::function_ref<bool(DWARFDIE die)> callback) {
425 Index();
426 m_set.globals.Find(regex, callback: DIERefCallback(callback, name: regex.GetText()));
427}
428
429void 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
435void 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
442void 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
450void 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
456void 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
465void 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
471void 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
518void ManualDWARFIndex::GetFunctions(
519 const RegularExpression &regex,
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
531void 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
553bool ManualDWARFIndex::Decode(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
571bool 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
579bool 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
584std::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
614bool 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
637void 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

source code of lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp