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>>
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>
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>
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 
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 
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 
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 
100 void DebugNamesDWARFIndex::GetGlobalVariables(
101     const RegularExpression &regex,
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 
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 
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 
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 
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 
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 
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 
261 void DebugNamesDWARFIndex::GetFunctions(
262     const RegularExpression &regex,
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 
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