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>>
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.GetAsLLVMDWARF(),
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>
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>
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().GetFileIndex(),
58                   DIERef::Section::DebugInfo, cu->GetOffset() + *die_offset);
59 
60   return std::nullopt;
61 }
62 
63 bool DebugNamesDWARFIndex::ProcessEntry(
64     const DebugNames::Entry &entry,
65     llvm::function_ref<bool(DWARFDIE die)> callback) {
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 
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 
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))
96       return;
97   }
98 
99   m_fallback.GetGlobalVariables(basename, callback);
100 }
101 
102 void DebugNamesDWARFIndex::GetGlobalVariables(
103     const RegularExpression &regex,
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       Mangled mangled_name(nte.getString());
108       if (!mangled_name.NameMatches(regex))
109         continue;
110 
111       uint64_t entry_offset = nte.getEntryOffset();
112       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
113       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
114         if (entry_or->tag() != DW_TAG_variable)
115           continue;
116 
117         if (!ProcessEntry(*entry_or, callback))
118           return;
119       }
120       MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());
121     }
122   }
123 
124   m_fallback.GetGlobalVariables(regex, callback);
125 }
126 
127 void DebugNamesDWARFIndex::GetGlobalVariables(
128     DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) {
129   uint64_t cu_offset = cu.GetOffset();
130   bool found_entry_for_cu = false;
131   for (const DebugNames::NameIndex &ni: *m_debug_names_up) {
132     for (DebugNames::NameTableEntry nte: ni) {
133       uint64_t entry_offset = nte.getEntryOffset();
134       llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);
135       for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {
136         if (entry_or->tag() != DW_TAG_variable)
137           continue;
138         if (entry_or->getCUOffset() != cu_offset)
139           continue;
140 
141         found_entry_for_cu = true;
142         if (!ProcessEntry(*entry_or, callback))
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     std::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))
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))
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     dwarf::Tag entry_tag = entry.tag();
231     if (entry_tag == DW_TAG_namespace ||
232         entry_tag == DW_TAG_imported_declaration) {
233       if (!ProcessEntry(entry, callback))
234         return;
235     }
236   }
237 
238   m_fallback.GetNamespaces(name, callback);
239 }
240 
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 
267 void DebugNamesDWARFIndex::GetFunctions(
268     const RegularExpression &regex,
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           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