LLDB mainline
AppleDWARFIndex.cpp
Go to the documentation of this file.
1//===-- AppleDWARFIndex.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
13
14#include "lldb/Core/Module.h"
17#include "llvm/Support/DJB.h"
18
19using namespace lldb;
20using namespace lldb_private;
21using namespace lldb_private::plugin::dwarf;
22using namespace llvm::dwarf;
23
24std::unique_ptr<AppleDWARFIndex> AppleDWARFIndex::Create(
25 Module &module, DWARFDataExtractor apple_names,
26 DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types,
27 DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str) {
28
29 llvm::DataExtractor llvm_debug_str = debug_str.GetAsLLVM();
30
31 auto apple_names_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
32 apple_names.GetAsLLVMDWARF(), llvm_debug_str);
33
34 auto apple_namespaces_table_up =
35 std::make_unique<llvm::AppleAcceleratorTable>(
36 apple_namespaces.GetAsLLVMDWARF(), llvm_debug_str);
37
38 auto apple_types_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
39 apple_types.GetAsLLVMDWARF(), llvm_debug_str);
40
41 auto apple_objc_table_up = std::make_unique<llvm::AppleAcceleratorTable>(
42 apple_objc.GetAsLLVMDWARF(), llvm_debug_str);
43
44 auto extract_and_check = [](auto &TablePtr) {
45 if (auto E = TablePtr->extract()) {
46 llvm::consumeError(std::move(E));
47 TablePtr.reset();
48 }
49 };
50
51 extract_and_check(apple_names_table_up);
52 extract_and_check(apple_namespaces_table_up);
53 extract_and_check(apple_types_table_up);
54 extract_and_check(apple_objc_table_up);
55 assert(apple_names.GetByteSize() == 0 || apple_names.GetSharedDataBuffer());
56 assert(apple_namespaces.GetByteSize() == 0 ||
57 apple_namespaces.GetSharedDataBuffer());
58 assert(apple_types.GetByteSize() == 0 || apple_types.GetSharedDataBuffer());
59 assert(apple_objc.GetByteSize() == 0 || apple_objc.GetSharedDataBuffer());
60
61 if (apple_names_table_up || apple_namespaces_table_up ||
62 apple_types_table_up || apple_objc_table_up)
63 return std::make_unique<AppleDWARFIndex>(
64 module, std::move(apple_names_table_up),
65 std::move(apple_namespaces_table_up), std::move(apple_types_table_up),
66 std::move(apple_objc_table_up), apple_names.GetSharedDataBuffer(),
67 apple_namespaces.GetSharedDataBuffer(),
68 apple_types.GetSharedDataBuffer(), apple_objc.GetSharedDataBuffer());
69
70 return nullptr;
71}
72
73/// Returns true if `tag` is a class_type of structure_type tag.
74static bool IsClassOrStruct(dw_tag_t tag) {
75 return tag == DW_TAG_class_type || tag == DW_TAG_structure_type;
76}
77
78/// Returns true if `entry` has an extractable DW_ATOM_qual_name_hash and it
79/// matches `expected_hash`.
80static bool
81EntryHasMatchingQualhash(const llvm::AppleAcceleratorTable::Entry &entry,
82 uint32_t expected_hash) {
83 std::optional<llvm::DWARFFormValue> form_value =
84 entry.lookup(llvm::dwarf::DW_ATOM_qual_name_hash);
85 if (!form_value)
86 return false;
87 std::optional<uint64_t> hash = form_value->getAsUnsignedConstant();
88 return hash && (*hash == expected_hash);
89}
90
91/// Returns true if `entry` has an extractable DW_ATOM_die_tag and it matches
92/// `expected_tag`. We also consider it a match if the tags are different but
93/// in the set of {TAG_class_type, TAG_struct_type}.
94static bool EntryHasMatchingTag(const llvm::AppleAcceleratorTable::Entry &entry,
95 dw_tag_t expected_tag) {
96 std::optional<llvm::DWARFFormValue> form_value =
97 entry.lookup(llvm::dwarf::DW_ATOM_die_tag);
98 if (!form_value)
99 return false;
100 std::optional<uint64_t> maybe_tag = form_value->getAsUnsignedConstant();
101 if (!maybe_tag)
102 return false;
103 auto tag = static_cast<dw_tag_t>(*maybe_tag);
104 return tag == expected_tag ||
105 (IsClassOrStruct(tag) && IsClassOrStruct(expected_tag));
106}
107
108/// Returns true if `entry` has an extractable DW_ATOM_type_flags and the flag
109/// "DW_FLAG_type_implementation" is set.
110static bool
111HasImplementationFlag(const llvm::AppleAcceleratorTable::Entry &entry) {
112 std::optional<llvm::DWARFFormValue> form_value =
113 entry.lookup(llvm::dwarf::DW_ATOM_type_flags);
114 if (!form_value)
115 return false;
116 std::optional<uint64_t> Flags = form_value->getAsUnsignedConstant();
117 return Flags &&
118 (*Flags & llvm::dwarf::AcceleratorTable::DW_FLAG_type_implementation);
119}
120
122 const llvm::AppleAcceleratorTable &table, llvm::StringRef name,
123 llvm::function_ref<IterationAction(DWARFDIE die)> callback,
124 std::optional<dw_tag_t> search_for_tag,
125 std::optional<uint32_t> search_for_qualhash) {
126 auto converted_cb = DIERefCallback(callback, name);
127 for (const auto &entry : table.equal_range(name)) {
128 if (search_for_qualhash &&
129 !EntryHasMatchingQualhash(entry, *search_for_qualhash))
130 continue;
131 if (search_for_tag && !EntryHasMatchingTag(entry, *search_for_tag))
132 continue;
133 if (converted_cb(entry) == IterationAction::Stop)
134 break;
135 }
136}
137
139 ConstString basename,
140 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
141 if (!m_apple_names_up)
142 return;
143 SearchFor(*m_apple_names_up, basename, callback);
144}
145
147 const RegularExpression &regex,
148 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
149 if (!m_apple_names_up)
150 return;
151
152 DIERefCallbackImpl converted_cb = DIERefCallback(callback, regex.GetText());
153
154 for (const auto &entry : m_apple_names_up->entries())
155 if (std::optional<llvm::StringRef> name = entry.readName();
156 name && Mangled(*name).NameMatches(regex))
157 if (converted_cb(entry.BaseEntry) == IterationAction::Stop)
158 return;
159}
160
162 DWARFUnit &cu, llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
163 if (!m_apple_names_up)
164 return;
165
166 const DWARFUnit &non_skeleton_cu = cu.GetNonSkeletonUnit();
167 dw_offset_t lower_bound = non_skeleton_cu.GetOffset();
168 dw_offset_t upper_bound = non_skeleton_cu.GetNextUnitOffset();
169 auto is_in_range = [lower_bound, upper_bound](std::optional<uint32_t> val) {
170 return val.has_value() && *val >= lower_bound && *val < upper_bound;
171 };
172
173 DIERefCallbackImpl converted_cb = DIERefCallback(callback);
174 for (auto entry : m_apple_names_up->entries()) {
175 if (is_in_range(entry.BaseEntry.getDIESectionOffset()))
176 if (converted_cb(entry.BaseEntry) == IterationAction::Stop)
177 return;
178 }
179}
180
182 ConstString class_name,
183 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
184 if (!m_apple_objc_up)
185 return;
186 SearchFor(*m_apple_objc_up, class_name, callback);
187}
188
190 ConstString class_name, bool must_be_implementation,
191 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
192 if (!m_apple_types_up)
193 return;
194
195 llvm::SmallVector<DIERef> decl_dies;
196 auto converted_cb = DIERefCallback(callback, class_name);
197
198 for (const auto &entry : m_apple_types_up->equal_range(class_name)) {
199 if (HasImplementationFlag(entry)) {
200 converted_cb(entry);
201 return;
202 }
203
204 decl_dies.emplace_back(std::nullopt, DIERef::Section::DebugInfo,
205 *entry.getDIESectionOffset());
206 }
207
208 if (must_be_implementation)
209 return;
210 for (DIERef ref : decl_dies)
211 if (converted_cb(ref) == IterationAction::Stop)
212 return;
213}
214
216 ConstString name,
217 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
218 if (!m_apple_types_up)
219 return;
220 SearchFor(*m_apple_types_up, name, callback);
221}
222
224 const DWARFDeclContext &context,
225 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
226 if (!m_apple_types_up)
227 return;
228
230 const bool entries_have_tag =
231 m_apple_types_up->containsAtomType(DW_ATOM_die_tag);
232 const bool entries_have_qual_hash =
233 m_apple_types_up->containsAtomType(DW_ATOM_qual_name_hash);
234
235 llvm::StringRef expected_name = context[0].name;
236
237 if (entries_have_tag && entries_have_qual_hash) {
238 const dw_tag_t expected_tag = context[0].tag;
239 const uint32_t expected_qualname_hash =
240 llvm::djbHash(context.GetQualifiedName());
241 if (log)
242 m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
243 SearchFor(*m_apple_types_up, expected_name, callback, expected_tag,
244 expected_qualname_hash);
245 return;
246 }
247
248 // Historically, if there are no tags, we also ignore qual_hash (why?)
249 if (!entries_have_tag) {
250 SearchFor(*m_apple_names_up, expected_name, callback);
251 return;
252 }
253
254 // We have a tag but no qual hash.
255
256 // When searching for a scoped type (for example,
257 // "std::vector<int>::const_iterator") searching for the innermost
258 // name alone ("const_iterator") could yield many false
259 // positives. By searching for the parent type ("vector<int>")
260 // first we can avoid extracting type DIEs from object files that
261 // would fail the filter anyway.
262 if ((context.GetSize() > 1) && IsClassOrStruct(context[1].tag))
263 if (m_apple_types_up->equal_range(context[1].name).empty())
264 return;
265
266 if (log)
267 m_module.LogMessage(log, "FindByNameAndTag()");
268 const dw_tag_t expected_tag = context[0].tag;
269 SearchFor(*m_apple_types_up, expected_name, callback, expected_tag);
270}
271
273 ConstString name,
274 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
276 return;
277 SearchFor(*m_apple_namespaces_up, name, callback);
278}
279
281 const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf,
282 const CompilerDeclContext &parent_decl_ctx,
283 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
284 if (!m_apple_names_up)
285 return;
286
287 ConstString name = lookup_info.GetLookupName();
288 for (const auto &entry : m_apple_names_up->equal_range(name)) {
289 DIERef die_ref(std::nullopt, DIERef::Section::DebugInfo,
290 *entry.getDIESectionOffset());
291 DWARFDIE die = dwarf.GetDIE(die_ref);
292 if (!die) {
293 ReportInvalidDIERef(die_ref, name);
294 continue;
295 }
296 if (ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, callback) ==
298 return;
299 }
300}
301
303 const RegularExpression &regex,
304 llvm::function_ref<IterationAction(DWARFDIE die)> callback) {
305 return GetGlobalVariables(regex, callback);
306}
307
310 s.PutCString(".apple_names index present\n");
312 s.PutCString(".apple_namespaces index present\n");
314 s.PutCString(".apple_types index present\n");
315 if (m_apple_objc_up)
316 s.PutCString(".apple_objc index present\n");
317 // TODO: Dump index contents
318}
static bool HasImplementationFlag(const llvm::AppleAcceleratorTable::Entry &entry)
Returns true if entry has an extractable DW_ATOM_type_flags and the flag "DW_FLAG_type_implementation...
static bool IsClassOrStruct(dw_tag_t tag)
Returns true if tag is a class_type of structure_type tag.
static bool EntryHasMatchingQualhash(const llvm::AppleAcceleratorTable::Entry &entry, uint32_t expected_hash)
Returns true if entry has an extractable DW_ATOM_qual_name_hash and it matches expected_hash.
static bool EntryHasMatchingTag(const llvm::AppleAcceleratorTable::Entry &entry, dw_tag_t expected_tag)
Returns true if entry has an extractable DW_ATOM_die_tag and it matches expected_tag.
Represents a generic declaration context in a program.
A uniqued constant string class.
Definition ConstString.h:40
llvm::DWARFDataExtractor GetAsLLVMDWARF() const
llvm::DataExtractor GetAsLLVM() const
lldb::DataBufferSP & GetSharedDataBuffer()
uint64_t GetByteSize() const
Get the number of bytes contained in this object.
A class to manage flags.
Definition Flags.h:22
A class that handles mangled names.
Definition Mangled.h:34
bool NameMatches(ConstString name) const
Check if "name" matches either the mangled or demangled name.
Definition Mangled.h:178
A class that encapsulates name lookup information.
Definition Module.h:916
ConstString GetLookupName() const
Definition Module.h:927
A class that describes an executable image and its associated object and symbol files.
Definition Module.h:90
llvm::StringRef GetText() const
Access the regular expression text.
A stream class that can stream formatted output to a file.
Definition Stream.h:28
size_t PutCString(llvm::StringRef cstr)
Output a C string to the stream.
Definition Stream.cpp:65
std::unique_ptr< llvm::AppleAcceleratorTable > m_apple_types_up
void SearchFor(const llvm::AppleAcceleratorTable &table, llvm::StringRef name, llvm::function_ref< IterationAction(DWARFDIE die)> callback, std::optional< dw_tag_t > search_for_tag=std::nullopt, std::optional< uint32_t > search_for_qualhash=std::nullopt)
}
static std::unique_ptr< AppleDWARFIndex > Create(Module &module, DWARFDataExtractor apple_names, DWARFDataExtractor apple_namespaces, DWARFDataExtractor apple_types, DWARFDataExtractor apple_objc, DWARFDataExtractor debug_str)
void GetNamespaces(ConstString name, llvm::function_ref< IterationAction(DWARFDIE die)> callback) override
void GetTypes(ConstString name, llvm::function_ref< IterationAction(DWARFDIE die)> callback) override
std::unique_ptr< llvm::AppleAcceleratorTable > m_apple_names_up
void GetCompleteObjCClass(ConstString class_name, bool must_be_implementation, llvm::function_ref< IterationAction(DWARFDIE die)> callback) override
void GetObjCMethods(ConstString class_name, llvm::function_ref< IterationAction(DWARFDIE die)> callback) override
void GetFunctions(const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, llvm::function_ref< IterationAction(DWARFDIE die)> callback) override
std::unique_ptr< llvm::AppleAcceleratorTable > m_apple_namespaces_up
std::unique_ptr< llvm::AppleAcceleratorTable > m_apple_objc_up
void GetGlobalVariables(ConstString basename, llvm::function_ref< IterationAction(DWARFDIE die)> callback) override
Finds global variables with the given base name.
Identifies a DWARF debug info entry within a given Module.
Definition DIERef.h:31
IterationAction ProcessFunctionDIE(const Module::LookupInfo &lookup_info, DWARFDIE die, const CompilerDeclContext &parent_decl_ctx, llvm::function_ref< IterationAction(DWARFDIE die)> callback)
Helper function implementing common logic for processing function dies.
DIERefCallbackImpl DIERefCallback(llvm::function_ref< IterationAction(DWARFDIE die)> callback, llvm::StringRef name={}) const
Definition DWARFIndex.h:128
void ReportInvalidDIERef(DIERef ref, llvm::StringRef name) const
dw_offset_t GetNextUnitOffset() const
Definition DWARFUnit.h:116
uint64_t dw_offset_t
Definition dwarf.h:24
llvm::dwarf::Tag dw_tag_t
Definition dwarf.h:19
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:332
IterationAction
Useful for callbacks whose return type indicates whether to continue iteration or short-circuit.