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