1 //===-- DebugNamesDWARFIndex.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/DebugNamesDWARFIndex.h"
10 #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"
11 #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
12 #include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"
13 #include "lldb/Core/Module.h"
14 #include "lldb/Utility/RegularExpression.h"
15 #include "lldb/Utility/Stream.h"
16 #include <optional>
17
18 using namespace lldb_private;
19 using namespace lldb;
20 using namespace lldb_private::dwarf;
21 using namespace lldb_private::plugin::dwarf;
22
23 llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>
Create(Module & module,DWARFDataExtractor debug_names,DWARFDataExtractor debug_str,SymbolFileDWARF & dwarf)24 DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names,
25 DWARFDataExtractor debug_str,
26 SymbolFileDWARF &dwarf) {
27 auto index_up = std::make_unique<DebugNames>(debug_names.GetAsLLVMDWARF(),
28 debug_str.GetAsLLVM());
29 if (llvm::Error E = index_up->extract())
30 return std::move(E);
31
32 return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex(
33 module, std::move(index_up), debug_names, debug_str, dwarf));
34 }
35
36 llvm::DenseSet<dw_offset_t>
GetUnits(const DebugNames & debug_names)37 DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) {
38 llvm::DenseSet<dw_offset_t> result;
39 for (const DebugNames::NameIndex &ni : debug_names) {
40 const uint32_t num_cus = ni.getCUCount();
41 for (uint32_t cu = 0; cu < num_cus; ++cu)
42 result.insert(ni.getCUOffset(cu));
43 const uint32_t num_tus = ni.getLocalTUCount();
44 for (uint32_t tu = 0; tu < num_tus; ++tu)
45 result.insert(ni.getLocalTUOffset(tu));
46 }
47 return result;
48 }
49
50 std::optional<DIERef>
ToDIERef(const DebugNames::Entry & entry) const51 DebugNamesDWARFIndex::ToDIERef(const DebugNames::Entry &entry) const {
52 // Look for a DWARF unit offset (CU offset or local TU offset) as they are
53 // both offsets into the .debug_info section.
54 std::optional<uint64_t> unit_offset = entry.getCUOffset();
55 if (!unit_offset) {
56 unit_offset = entry.getLocalTUOffset();
57 if (!unit_offset)
58 return std::nullopt;
59 }
60
61 DWARFUnit *cu =
62 m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *unit_offset);
63 if (!cu)
64 return std::nullopt;
65
66 cu = &cu->GetNonSkeletonUnit();
67 if (std::optional<uint64_t> die_offset = entry.getDIEUnitOffset())
68 return DIERef(cu->GetSymbolFileDWARF().GetFileIndex(),
69 DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset);
70
71 return std::nullopt;
72 }
73
ProcessEntry(const DebugNames::Entry & entry,llvm::function_ref<bool (DWARFDIE die)> callback)74 bool DebugNamesDWARFIndex::ProcessEntry(
75 const DebugNames::Entry &entry,
76 llvm::function_ref<bool(DWARFDIE die)> callback) {
77 std::optional<DIERef> ref = ToDIERef(entry);
78 if (!ref)
79 return true;
80 SymbolFileDWARF &dwarf = *llvm::cast<SymbolFileDWARF>(
81 m_module.GetSymbolFile()->GetBackingSymbolFile());
82 DWARFDIE die = dwarf.GetDIE(*ref);
83 if (!die)
84 return true;
85 return callback(die);
86 }
87
MaybeLogLookupError(llvm::Error error,const DebugNames::NameIndex & ni,llvm::StringRef name)88 void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error,
89 const DebugNames::NameIndex &ni,
90 llvm::StringRef name) {
91 // Ignore SentinelErrors, log everything else.
92 LLDB_LOG_ERROR(
93 GetLog(DWARFLog::Lookups),
94 handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}),
95 "Failed to parse index entries for index at {1:x}, name {2}: {0}",
96 ni.getUnitOffset(), name);
97 }
98
GetGlobalVariables(ConstString basename,llvm::function_ref<bool (DWARFDIE die)> callback)99 void DebugNamesDWARFIndex::GetGlobalVariables(
100 ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) {
101 for (const DebugNames::Entry &entry :
102 m_debug_names_up->equal_range(basename.GetStringRef())) {
103 if (entry.tag() != DW_TAG_variable)
104 continue;
105
106 if (!ProcessEntry(entry, callback))
107 return;
108 }
109
110 m_fallback.GetGlobalVariables(basename, callback);
111 }
112
GetGlobalVariables(const RegularExpression & regex,llvm::function_ref<bool (DWARFDIE die)> callback)113 void DebugNamesDWARFIndex::GetGlobalVariables(
114 const RegularExpression ®ex,
115 llvm::function_ref<bool(DWARFDIE die)> callback) {
116 for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
117 for (DebugNames::NameTableEntry nte: ni) {
118 Mangled mangled_name(nte.getString());
119 if (!mangled_name.NameMatches(regex))
120 continue;
121
122 uint64_t entry_offset = nte.getEntryOffset();
123 llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
124 for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
125 if (entry_or->tag() != DW_TAG_variable)
126 continue;
127
128 if (!ProcessEntry(*entry_or, callback))
129 return;
130 }
131 MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
132 }
133 }
134
135 m_fallback.GetGlobalVariables(regex, callback);
136 }
137
GetGlobalVariables(DWARFUnit & cu,llvm::function_ref<bool (DWARFDIE die)> callback)138 void DebugNamesDWARFIndex::GetGlobalVariables(
139 DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) {
140 uint64_t cu_offset = cu.GetOffset();
141 bool found_entry_for_cu = false;
142 for (const DebugNames::NameIndex &ni : *m_debug_names_up) {
143 // Check if this name index contains an entry for the given CU.
144 bool cu_matches = false;
145 for (uint32_t i = 0; i < ni.getCUCount(); ++i) {
146 if (ni.getCUOffset(i) == cu_offset) {
147 cu_matches = true;
148 break;
149 }
150 }
151 if (!cu_matches)
152 continue;
153
154 for (DebugNames::NameTableEntry nte : ni) {
155 uint64_t entry_offset = nte.getEntryOffset();
156 llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
157 for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
158 if (entry_or->tag() != DW_TAG_variable)
159 continue;
160 if (entry_or->getCUOffset() != cu_offset)
161 continue;
162
163 found_entry_for_cu = true;
164 if (!ProcessEntry(*entry_or, callback))
165 return;
166 }
167 MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
168 }
169 }
170 // If no name index for that particular CU was found, fallback to
171 // creating the manual index.
172 if (!found_entry_for_cu)
173 m_fallback.GetGlobalVariables(cu, callback);
174 }
175
GetCompleteObjCClass(ConstString class_name,bool must_be_implementation,llvm::function_ref<bool (DWARFDIE die)> callback)176 void DebugNamesDWARFIndex::GetCompleteObjCClass(
177 ConstString class_name, bool must_be_implementation,
178 llvm::function_ref<bool(DWARFDIE die)> callback) {
179 // Keep a list of incomplete types as fallback for when we don't find the
180 // complete type.
181 DIEArray incomplete_types;
182
183 for (const DebugNames::Entry &entry :
184 m_debug_names_up->equal_range(class_name.GetStringRef())) {
185 if (entry.tag() != DW_TAG_structure_type &&
186 entry.tag() != DW_TAG_class_type)
187 continue;
188
189 std::optional<DIERef> ref = ToDIERef(entry);
190 if (!ref)
191 continue;
192
193 DWARFUnit *cu = m_debug_info.GetUnit(*ref);
194 if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) {
195 incomplete_types.push_back(*ref);
196 continue;
197 }
198
199 DWARFDIE die = m_debug_info.GetDIE(*ref);
200 if (!die) {
201 ReportInvalidDIERef(*ref, class_name.GetStringRef());
202 continue;
203 }
204
205 if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) {
206 // If we find the complete version we're done.
207 callback(die);
208 return;
209 }
210 incomplete_types.push_back(*ref);
211 }
212
213 auto dierefcallback = DIERefCallback(callback, class_name.GetStringRef());
214 for (DIERef ref : incomplete_types)
215 if (!dierefcallback(ref))
216 return;
217
218 m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback);
219 }
220
GetTypes(ConstString name,llvm::function_ref<bool (DWARFDIE die)> callback)221 void DebugNamesDWARFIndex::GetTypes(
222 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
223 for (const DebugNames::Entry &entry :
224 m_debug_names_up->equal_range(name.GetStringRef())) {
225 if (isType(entry.tag())) {
226 if (!ProcessEntry(entry, callback))
227 return;
228 }
229 }
230
231 m_fallback.GetTypes(name, callback);
232 }
233
GetTypes(const DWARFDeclContext & context,llvm::function_ref<bool (DWARFDIE die)> callback)234 void DebugNamesDWARFIndex::GetTypes(
235 const DWARFDeclContext &context,
236 llvm::function_ref<bool(DWARFDIE die)> callback) {
237 auto name = context[0].name;
238 for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) {
239 if (entry.tag() == context[0].tag) {
240 if (!ProcessEntry(entry, callback))
241 return;
242 }
243 }
244
245 m_fallback.GetTypes(context, callback);
246 }
247
GetNamespaces(ConstString name,llvm::function_ref<bool (DWARFDIE die)> callback)248 void DebugNamesDWARFIndex::GetNamespaces(
249 ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
250 for (const DebugNames::Entry &entry :
251 m_debug_names_up->equal_range(name.GetStringRef())) {
252 lldb_private::dwarf::Tag entry_tag = entry.tag();
253 if (entry_tag == DW_TAG_namespace ||
254 entry_tag == DW_TAG_imported_declaration) {
255 if (!ProcessEntry(entry, callback))
256 return;
257 }
258 }
259
260 m_fallback.GetNamespaces(name, callback);
261 }
262
GetFunctions(const Module::LookupInfo & lookup_info,SymbolFileDWARF & dwarf,const CompilerDeclContext & parent_decl_ctx,llvm::function_ref<bool (DWARFDIE die)> callback)263 void DebugNamesDWARFIndex::GetFunctions(
264 const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf,
265 const CompilerDeclContext &parent_decl_ctx,
266 llvm::function_ref<bool(DWARFDIE die)> callback) {
267 ConstString name = lookup_info.GetLookupName();
268 std::set<DWARFDebugInfoEntry *> seen;
269 for (const DebugNames::Entry &entry :
270 m_debug_names_up->equal_range(name.GetStringRef())) {
271 Tag tag = entry.tag();
272 if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
273 continue;
274
275 if (std::optional<DIERef> ref = ToDIERef(entry)) {
276 if (!ProcessFunctionDIE(lookup_info, *ref, dwarf, parent_decl_ctx,
277 [&](DWARFDIE die) {
278 if (!seen.insert(die.GetDIE()).second)
279 return true;
280 return callback(die);
281 }))
282 return;
283 }
284 }
285
286 m_fallback.GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback);
287 }
288
GetFunctions(const RegularExpression & regex,llvm::function_ref<bool (DWARFDIE die)> callback)289 void DebugNamesDWARFIndex::GetFunctions(
290 const RegularExpression ®ex,
291 llvm::function_ref<bool(DWARFDIE die)> callback) {
292 for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
293 for (DebugNames::NameTableEntry nte: ni) {
294 if (!regex.Execute(nte.getString()))
295 continue;
296
297 uint64_t entry_offset = nte.getEntryOffset();
298 llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
299 for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
300 Tag tag = entry_or->tag();
301 if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
302 continue;
303
304 if (!ProcessEntry(*entry_or, callback))
305 return;
306 }
307 MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
308 }
309 }
310
311 m_fallback.GetFunctions(regex, callback);
312 }
313
Dump(Stream & s)314 void DebugNamesDWARFIndex::Dump(Stream &s) {
315 m_fallback.Dump(s);
316
317 std::string data;
318 llvm::raw_string_ostream os(data);
319 m_debug_names_up->dump(os);
320 s.PutCString(os.str());
321 }
322