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