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