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       GetLog(DWARFLog::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     DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) {
127   lldbassert(!cu.GetSymbolFileDWARF().GetDwoNum());
128   uint64_t cu_offset = cu.GetOffset();
129   bool found_entry_for_cu = false;
130   for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
131     for (DebugNames::NameTableEntry nte: ni) {
132       uint64_t entry_offset = nte.getEntryOffset();
133       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
134       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
135         if (entry_or->tag() != DW_TAG_variable)
136           continue;
137         if (entry_or->getCUOffset() != cu_offset)
138           continue;
139 
140         found_entry_for_cu = true;
141         if (!ProcessEntry(*entry_or, callback,
142                           llvm::StringRef(nte.getString())))
143           return;
144       }
145       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
146     }
147   }
148   // If no name index for that particular CU was found, fallback to
149   // creating the manual index.
150   if (!found_entry_for_cu)
151     m_fallback.GetGlobalVariables(cu, callback);
152 }
153 
154 void DebugNamesDWARFIndex::GetCompleteObjCClass(
155     ConstString class_name, bool must_be_implementation,
156     llvm::function_ref<bool(DWARFDIE die)> callback) {
157   // Keep a list of incomplete types as fallback for when we don't find the
158   // complete type.
159   DIEArray incomplete_types;
160 
161   for (const DebugNames::Entry &entry :
162        m_debug_names_up->equal_range(class_name.GetStringRef())) {
163     if (entry.tag() != DW_TAG_structure_type &&
164         entry.tag() != DW_TAG_class_type)
165       continue;
166 
167     llvm::Optional<DIERef> ref = ToDIERef(entry);
168     if (!ref)
169       continue;
170 
171     DWARFUnit *cu = m_debug_info.GetUnit(*ref);
172     if (!cu || !cu->Supports_DW_AT_APPLE_objc_complete_type()) {
173       incomplete_types.push_back(*ref);
174       continue;
175     }
176 
177     DWARFDIE die = m_debug_info.GetDIE(*ref);
178     if (!die) {
179       ReportInvalidDIERef(*ref, class_name.GetStringRef());
180       continue;
181     }
182 
183     if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) {
184       // If we find the complete version we're done.
185       callback(die);
186       return;
187     }
188     incomplete_types.push_back(*ref);
189   }
190 
191   auto dierefcallback = DIERefCallback(callback, class_name.GetStringRef());
192   for (DIERef ref : incomplete_types)
193     if (!dierefcallback(ref))
194       return;
195 
196   m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback);
197 }
198 
199 void DebugNamesDWARFIndex::GetTypes(
200     ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
201   for (const DebugNames::Entry &entry :
202        m_debug_names_up->equal_range(name.GetStringRef())) {
203     if (isType(entry.tag())) {
204       if (!ProcessEntry(entry, callback, name.GetStringRef()))
205         return;
206     }
207   }
208 
209   m_fallback.GetTypes(name, callback);
210 }
211 
212 void DebugNamesDWARFIndex::GetTypes(
213     const DWARFDeclContext &context,
214     llvm::function_ref<bool(DWARFDIE die)> callback) {
215   auto name = context[0].name;
216   for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) {
217     if (entry.tag() == context[0].tag) {
218       if (!ProcessEntry(entry, callback, name))
219         return;
220     }
221   }
222 
223   m_fallback.GetTypes(context, callback);
224 }
225 
226 void DebugNamesDWARFIndex::GetNamespaces(
227     ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {
228   for (const DebugNames::Entry &entry :
229        m_debug_names_up->equal_range(name.GetStringRef())) {
230     if (entry.tag() == DW_TAG_namespace) {
231       if (!ProcessEntry(entry, callback, name.GetStringRef()))
232         return;
233     }
234   }
235 
236   m_fallback.GetNamespaces(name, callback);
237 }
238 
239 void DebugNamesDWARFIndex::GetFunctions(
240     ConstString name, SymbolFileDWARF &dwarf,
241     const CompilerDeclContext &parent_decl_ctx, uint32_t name_type_mask,
242     llvm::function_ref<bool(DWARFDIE die)> callback) {
243 
244   std::set<DWARFDebugInfoEntry *> seen;
245   for (const DebugNames::Entry &entry :
246        m_debug_names_up->equal_range(name.GetStringRef())) {
247     Tag tag = entry.tag();
248     if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
249       continue;
250 
251     if (llvm::Optional<DIERef> ref = ToDIERef(entry)) {
252       if (!ProcessFunctionDIE(name.GetStringRef(), *ref, dwarf, parent_decl_ctx,
253                               name_type_mask, [&](DWARFDIE die) {
254                                 if (!seen.insert(die.GetDIE()).second)
255                                   return true;
256                                 return callback(die);
257                               }))
258         return;
259     }
260   }
261 
262   m_fallback.GetFunctions(name, dwarf, parent_decl_ctx, name_type_mask,
263                           callback);
264 }
265 
266 void DebugNamesDWARFIndex::GetFunctions(
267     const RegularExpression &regex,
268     llvm::function_ref<bool(DWARFDIE die)> callback) {
269   for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
270     for (DebugNames::NameTableEntry nte: ni) {
271       if (!regex.Execute(nte.getString()))
272         continue;
273 
274       uint64_t entry_offset = nte.getEntryOffset();
275       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
276       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
277         Tag tag = entry_or->tag();
278         if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)
279           continue;
280 
281         if (!ProcessEntry(*entry_or, callback,
282                           llvm::StringRef(nte.getString())))
283           return;
284       }
285       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
286     }
287   }
288 
289   m_fallback.GetFunctions(regex, callback);
290 }
291 
292 void DebugNamesDWARFIndex::Dump(Stream &s) {
293   m_fallback.Dump(s);
294 
295   std::string data;
296   llvm::raw_string_ostream os(data);
297   m_debug_names_up->dump(os);
298   s.PutCString(os.str());
299 }
300