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