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