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