1 | //===-- BreakpointResolverName.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 "lldb/Breakpoint/BreakpointResolverName.h" |
10 | |
11 | #include "lldb/Breakpoint/BreakpointLocation.h" |
12 | #include "lldb/Core/Architecture.h" |
13 | #include "lldb/Core/Module.h" |
14 | #include "lldb/Symbol/Block.h" |
15 | #include "lldb/Symbol/Function.h" |
16 | #include "lldb/Symbol/Symbol.h" |
17 | #include "lldb/Symbol/SymbolContext.h" |
18 | #include "lldb/Target/Language.h" |
19 | #include "lldb/Target/Target.h" |
20 | #include "lldb/Utility/LLDBLog.h" |
21 | #include "lldb/Utility/Log.h" |
22 | #include "lldb/Utility/StreamString.h" |
23 | |
24 | using namespace lldb; |
25 | using namespace lldb_private; |
26 | |
27 | BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt, |
28 | const char *name_cstr, FunctionNameType name_type_mask, |
29 | LanguageType language, Breakpoint::MatchType type, lldb::addr_t offset, |
30 | bool skip_prologue) |
31 | : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset), |
32 | m_match_type(type), m_language(language), m_skip_prologue(skip_prologue) { |
33 | if (m_match_type == Breakpoint::Regexp) { |
34 | m_regex = RegularExpression(name_cstr); |
35 | if (!m_regex.IsValid()) { |
36 | Log *log = GetLog(mask: LLDBLog::Breakpoints); |
37 | |
38 | if (log) |
39 | log->Warning(fmt: "function name regexp: \"%s\" did not compile." , |
40 | name_cstr); |
41 | } |
42 | } else { |
43 | AddNameLookup(name: ConstString(name_cstr), name_type_mask); |
44 | } |
45 | } |
46 | |
47 | BreakpointResolverName::BreakpointResolverName( |
48 | const BreakpointSP &bkpt, const char *names[], size_t num_names, |
49 | FunctionNameType name_type_mask, LanguageType language, lldb::addr_t offset, |
50 | bool skip_prologue) |
51 | : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset), |
52 | m_match_type(Breakpoint::Exact), m_language(language), |
53 | m_skip_prologue(skip_prologue) { |
54 | for (size_t i = 0; i < num_names; i++) { |
55 | AddNameLookup(name: ConstString(names[i]), name_type_mask); |
56 | } |
57 | } |
58 | |
59 | BreakpointResolverName::BreakpointResolverName( |
60 | const BreakpointSP &bkpt, const std::vector<std::string> &names, |
61 | FunctionNameType name_type_mask, LanguageType language, lldb::addr_t offset, |
62 | bool skip_prologue) |
63 | : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset), |
64 | m_match_type(Breakpoint::Exact), m_language(language), |
65 | m_skip_prologue(skip_prologue) { |
66 | for (const std::string &name : names) { |
67 | AddNameLookup(name: ConstString(name.c_str(), name.size()), name_type_mask); |
68 | } |
69 | } |
70 | |
71 | BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt, |
72 | RegularExpression func_regex, |
73 | lldb::LanguageType language, |
74 | lldb::addr_t offset, |
75 | bool skip_prologue) |
76 | : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset), |
77 | m_class_name(nullptr), m_regex(std::move(func_regex)), |
78 | m_match_type(Breakpoint::Regexp), m_language(language), |
79 | m_skip_prologue(skip_prologue) {} |
80 | |
81 | BreakpointResolverName::BreakpointResolverName( |
82 | const BreakpointResolverName &rhs) |
83 | : BreakpointResolver(rhs.GetBreakpoint(), BreakpointResolver::NameResolver, |
84 | rhs.GetOffset()), |
85 | m_lookups(rhs.m_lookups), m_class_name(rhs.m_class_name), |
86 | m_regex(rhs.m_regex), m_match_type(rhs.m_match_type), |
87 | m_language(rhs.m_language), m_skip_prologue(rhs.m_skip_prologue) {} |
88 | |
89 | BreakpointResolverSP BreakpointResolverName::CreateFromStructuredData( |
90 | const StructuredData::Dictionary &options_dict, Status &error) { |
91 | LanguageType language = eLanguageTypeUnknown; |
92 | llvm::StringRef language_name; |
93 | bool success = options_dict.GetValueForKeyAsString( |
94 | key: GetKey(enum_value: OptionNames::LanguageName), result&: language_name); |
95 | if (success) { |
96 | language = Language::GetLanguageTypeFromString(string: language_name); |
97 | if (language == eLanguageTypeUnknown) { |
98 | error.SetErrorStringWithFormatv(format: "BRN::CFSD: Unknown language: {0}." , |
99 | args&: language_name); |
100 | return nullptr; |
101 | } |
102 | } |
103 | |
104 | lldb::offset_t offset = 0; |
105 | success = |
106 | options_dict.GetValueForKeyAsInteger(key: GetKey(enum_value: OptionNames::Offset), result&: offset); |
107 | if (!success) { |
108 | error.SetErrorString("BRN::CFSD: Missing offset entry." ); |
109 | return nullptr; |
110 | } |
111 | |
112 | bool skip_prologue; |
113 | success = options_dict.GetValueForKeyAsBoolean( |
114 | key: GetKey(enum_value: OptionNames::SkipPrologue), result&: skip_prologue); |
115 | if (!success) { |
116 | error.SetErrorString("BRN::CFSD: Missing Skip prologue entry." ); |
117 | return nullptr; |
118 | } |
119 | |
120 | llvm::StringRef regex_text; |
121 | success = options_dict.GetValueForKeyAsString( |
122 | key: GetKey(enum_value: OptionNames::RegexString), result&: regex_text); |
123 | if (success) { |
124 | return std::make_shared<BreakpointResolverName>( |
125 | args: nullptr, args: RegularExpression(regex_text), args&: language, args&: offset, |
126 | args&: skip_prologue); |
127 | } else { |
128 | StructuredData::Array *names_array; |
129 | success = options_dict.GetValueForKeyAsArray( |
130 | key: GetKey(enum_value: OptionNames::SymbolNameArray), result&: names_array); |
131 | if (!success) { |
132 | error.SetErrorString("BRN::CFSD: Missing symbol names entry." ); |
133 | return nullptr; |
134 | } |
135 | StructuredData::Array *names_mask_array; |
136 | success = options_dict.GetValueForKeyAsArray( |
137 | key: GetKey(enum_value: OptionNames::NameMaskArray), result&: names_mask_array); |
138 | if (!success) { |
139 | error.SetErrorString("BRN::CFSD: Missing symbol names mask entry." ); |
140 | return nullptr; |
141 | } |
142 | |
143 | size_t num_elem = names_array->GetSize(); |
144 | if (num_elem != names_mask_array->GetSize()) { |
145 | error.SetErrorString( |
146 | "BRN::CFSD: names and names mask arrays have different sizes." ); |
147 | return nullptr; |
148 | } |
149 | |
150 | if (num_elem == 0) { |
151 | error.SetErrorString( |
152 | "BRN::CFSD: no name entry in a breakpoint by name breakpoint." ); |
153 | return nullptr; |
154 | } |
155 | std::vector<std::string> names; |
156 | std::vector<FunctionNameType> name_masks; |
157 | for (size_t i = 0; i < num_elem; i++) { |
158 | std::optional<llvm::StringRef> maybe_name = |
159 | names_array->GetItemAtIndexAsString(idx: i); |
160 | if (!maybe_name) { |
161 | error.SetErrorString("BRN::CFSD: name entry is not a string." ); |
162 | return nullptr; |
163 | } |
164 | auto maybe_fnt = names_mask_array->GetItemAtIndexAsInteger< |
165 | std::underlying_type<FunctionNameType>::type>(idx: i); |
166 | if (!maybe_fnt) { |
167 | error.SetErrorString("BRN::CFSD: name mask entry is not an integer." ); |
168 | return nullptr; |
169 | } |
170 | names.push_back(x: std::string(*maybe_name)); |
171 | name_masks.push_back(x: static_cast<FunctionNameType>(*maybe_fnt)); |
172 | } |
173 | |
174 | std::shared_ptr<BreakpointResolverName> resolver_sp = |
175 | std::make_shared<BreakpointResolverName>( |
176 | args: nullptr, args: names[0].c_str(), args&: name_masks[0], args&: language, |
177 | args: Breakpoint::MatchType::Exact, args&: offset, args&: skip_prologue); |
178 | for (size_t i = 1; i < num_elem; i++) { |
179 | resolver_sp->AddNameLookup(name: ConstString(names[i]), name_type_mask: name_masks[i]); |
180 | } |
181 | return resolver_sp; |
182 | } |
183 | } |
184 | |
185 | StructuredData::ObjectSP BreakpointResolverName::SerializeToStructuredData() { |
186 | StructuredData::DictionarySP options_dict_sp( |
187 | new StructuredData::Dictionary()); |
188 | |
189 | if (m_regex.IsValid()) { |
190 | options_dict_sp->AddStringItem(key: GetKey(enum_value: OptionNames::RegexString), |
191 | value: m_regex.GetText()); |
192 | } else { |
193 | StructuredData::ArraySP names_sp(new StructuredData::Array()); |
194 | StructuredData::ArraySP name_masks_sp(new StructuredData::Array()); |
195 | for (auto lookup : m_lookups) { |
196 | names_sp->AddItem(item: StructuredData::StringSP( |
197 | new StructuredData::String(lookup.GetName().GetStringRef()))); |
198 | name_masks_sp->AddItem(item: StructuredData::UnsignedIntegerSP( |
199 | new StructuredData::UnsignedInteger(lookup.GetNameTypeMask()))); |
200 | } |
201 | options_dict_sp->AddItem(key: GetKey(enum_value: OptionNames::SymbolNameArray), value_sp: names_sp); |
202 | options_dict_sp->AddItem(key: GetKey(enum_value: OptionNames::NameMaskArray), value_sp: name_masks_sp); |
203 | } |
204 | if (m_language != eLanguageTypeUnknown) |
205 | options_dict_sp->AddStringItem( |
206 | key: GetKey(enum_value: OptionNames::LanguageName), |
207 | value: Language::GetNameForLanguageType(language: m_language)); |
208 | options_dict_sp->AddBooleanItem(key: GetKey(enum_value: OptionNames::SkipPrologue), |
209 | value: m_skip_prologue); |
210 | |
211 | return WrapOptionsDict(options_dict_sp); |
212 | } |
213 | |
214 | void BreakpointResolverName::AddNameLookup(ConstString name, |
215 | FunctionNameType name_type_mask) { |
216 | |
217 | Module::LookupInfo lookup(name, name_type_mask, m_language); |
218 | m_lookups.emplace_back(args&: lookup); |
219 | |
220 | auto add_variant_funcs = [&](Language *lang) { |
221 | for (Language::MethodNameVariant variant : |
222 | lang->GetMethodNameVariants(method_name: name)) { |
223 | // FIXME: Should we be adding variants that aren't of type Full? |
224 | if (variant.GetType() & lldb::eFunctionNameTypeFull) { |
225 | Module::LookupInfo variant_lookup(name, variant.GetType(), |
226 | lang->GetLanguageType()); |
227 | variant_lookup.SetLookupName(variant.GetName()); |
228 | m_lookups.emplace_back(args&: variant_lookup); |
229 | } |
230 | } |
231 | return true; |
232 | }; |
233 | |
234 | if (Language *lang = Language::FindPlugin(language: m_language)) { |
235 | add_variant_funcs(lang); |
236 | } else { |
237 | // Most likely m_language is eLanguageTypeUnknown. We check each language for |
238 | // possible variants or more qualified names and create lookups for those as |
239 | // well. |
240 | Language::ForEach(callback: add_variant_funcs); |
241 | } |
242 | } |
243 | |
244 | // FIXME: Right now we look at the module level, and call the module's |
245 | // "FindFunctions". |
246 | // Greg says he will add function tables, maybe at the CompileUnit level to |
247 | // accelerate function lookup. At that point, we should switch the depth to |
248 | // CompileUnit, and look in these tables. |
249 | |
250 | Searcher::CallbackReturn |
251 | BreakpointResolverName::SearchCallback(SearchFilter &filter, |
252 | SymbolContext &context, Address *addr) { |
253 | Log *log = GetLog(mask: LLDBLog::Breakpoints); |
254 | |
255 | if (m_class_name) { |
256 | if (log) |
257 | log->Warning(fmt: "Class/method function specification not supported yet.\n" ); |
258 | return Searcher::eCallbackReturnStop; |
259 | } |
260 | |
261 | SymbolContextList func_list; |
262 | bool filter_by_cu = |
263 | (filter.GetFilterRequiredItems() & eSymbolContextCompUnit) != 0; |
264 | bool filter_by_language = (m_language != eLanguageTypeUnknown); |
265 | |
266 | ModuleFunctionSearchOptions function_options; |
267 | function_options.include_symbols = !filter_by_cu; |
268 | function_options.include_inlines = true; |
269 | |
270 | switch (m_match_type) { |
271 | case Breakpoint::Exact: |
272 | if (context.module_sp) { |
273 | for (const auto &lookup : m_lookups) { |
274 | const size_t start_func_idx = func_list.GetSize(); |
275 | context.module_sp->FindFunctions(lookup_info: lookup, parent_decl_ctx: CompilerDeclContext(), |
276 | options: function_options, sc_list&: func_list); |
277 | |
278 | const size_t end_func_idx = func_list.GetSize(); |
279 | |
280 | if (start_func_idx < end_func_idx) |
281 | lookup.Prune(sc_list&: func_list, start_idx: start_func_idx); |
282 | } |
283 | } |
284 | break; |
285 | case Breakpoint::Regexp: |
286 | if (context.module_sp) { |
287 | context.module_sp->FindFunctions(regex: m_regex, options: function_options, sc_list&: func_list); |
288 | } |
289 | break; |
290 | case Breakpoint::Glob: |
291 | if (log) |
292 | log->Warning(fmt: "glob is not supported yet." ); |
293 | break; |
294 | } |
295 | |
296 | // If the filter specifies a Compilation Unit, remove the ones that don't |
297 | // pass at this point. |
298 | if (filter_by_cu || filter_by_language) { |
299 | uint32_t num_functions = func_list.GetSize(); |
300 | |
301 | for (size_t idx = 0; idx < num_functions; idx++) { |
302 | bool remove_it = false; |
303 | SymbolContext sc; |
304 | func_list.GetContextAtIndex(idx, sc); |
305 | if (filter_by_cu) { |
306 | if (!sc.comp_unit || !filter.CompUnitPasses(compUnit&: *sc.comp_unit)) |
307 | remove_it = true; |
308 | } |
309 | |
310 | if (filter_by_language) { |
311 | LanguageType sym_language = sc.GetLanguage(); |
312 | if ((Language::GetPrimaryLanguage(language: sym_language) != |
313 | Language::GetPrimaryLanguage(language: m_language)) && |
314 | (sym_language != eLanguageTypeUnknown)) { |
315 | remove_it = true; |
316 | } |
317 | } |
318 | |
319 | if (remove_it) { |
320 | func_list.RemoveContextAtIndex(idx); |
321 | num_functions--; |
322 | idx--; |
323 | } |
324 | } |
325 | } |
326 | |
327 | BreakpointSP breakpoint_sp = GetBreakpoint(); |
328 | Breakpoint &breakpoint = *breakpoint_sp; |
329 | Address break_addr; |
330 | |
331 | // Remove any duplicates between the function list and the symbol list |
332 | for (const SymbolContext &sc : func_list) { |
333 | bool is_reexported = false; |
334 | |
335 | if (sc.block && sc.block->GetInlinedFunctionInfo()) { |
336 | if (!sc.block->GetStartAddress(addr&: break_addr)) |
337 | break_addr.Clear(); |
338 | } else if (sc.function) { |
339 | break_addr = sc.function->GetAddressRange().GetBaseAddress(); |
340 | if (m_skip_prologue && break_addr.IsValid()) { |
341 | const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize(); |
342 | if (prologue_byte_size) |
343 | break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); |
344 | } |
345 | } else if (sc.symbol) { |
346 | if (sc.symbol->GetType() == eSymbolTypeReExported) { |
347 | const Symbol *actual_symbol = |
348 | sc.symbol->ResolveReExportedSymbol(target&: breakpoint.GetTarget()); |
349 | if (actual_symbol) { |
350 | is_reexported = true; |
351 | break_addr = actual_symbol->GetAddress(); |
352 | } |
353 | } else { |
354 | break_addr = sc.symbol->GetAddress(); |
355 | } |
356 | |
357 | if (m_skip_prologue && break_addr.IsValid()) { |
358 | const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize(); |
359 | if (prologue_byte_size) |
360 | break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); |
361 | else { |
362 | const Architecture *arch = |
363 | breakpoint.GetTarget().GetArchitecturePlugin(); |
364 | if (arch) |
365 | arch->AdjustBreakpointAddress(func: *sc.symbol, addr&: break_addr); |
366 | } |
367 | } |
368 | } |
369 | |
370 | if (!break_addr.IsValid()) |
371 | continue; |
372 | |
373 | if (!filter.AddressPasses(addr&: break_addr)) |
374 | continue; |
375 | |
376 | bool new_location; |
377 | BreakpointLocationSP bp_loc_sp(AddLocation(loc_addr: break_addr, new_location: &new_location)); |
378 | bp_loc_sp->SetIsReExported(is_reexported); |
379 | if (bp_loc_sp && new_location && !breakpoint.IsInternal()) { |
380 | if (log) { |
381 | StreamString s; |
382 | bp_loc_sp->GetDescription(s: &s, level: lldb::eDescriptionLevelVerbose); |
383 | LLDB_LOGF(log, "Added location: %s\n" , s.GetData()); |
384 | } |
385 | } |
386 | } |
387 | |
388 | return Searcher::eCallbackReturnContinue; |
389 | } |
390 | |
391 | lldb::SearchDepth BreakpointResolverName::GetDepth() { |
392 | return lldb::eSearchDepthModule; |
393 | } |
394 | |
395 | void BreakpointResolverName::GetDescription(Stream *s) { |
396 | if (m_match_type == Breakpoint::Regexp) |
397 | s->Printf(format: "regex = '%s'" , m_regex.GetText().str().c_str()); |
398 | else { |
399 | size_t num_names = m_lookups.size(); |
400 | if (num_names == 1) |
401 | s->Printf(format: "name = '%s'" , m_lookups[0].GetName().GetCString()); |
402 | else { |
403 | s->Printf(format: "names = {" ); |
404 | for (size_t i = 0; i < num_names; i++) { |
405 | s->Printf(format: "%s'%s'" , (i == 0 ? "" : ", " ), |
406 | m_lookups[i].GetName().GetCString()); |
407 | } |
408 | s->Printf(format: "}" ); |
409 | } |
410 | } |
411 | if (m_language != eLanguageTypeUnknown) { |
412 | s->Printf(format: ", language = %s" , Language::GetNameForLanguageType(language: m_language)); |
413 | } |
414 | } |
415 | |
416 | void BreakpointResolverName::Dump(Stream *s) const {} |
417 | |
418 | lldb::BreakpointResolverSP |
419 | BreakpointResolverName::CopyForBreakpoint(BreakpointSP &breakpoint) { |
420 | lldb::BreakpointResolverSP ret_sp(new BreakpointResolverName(*this)); |
421 | ret_sp->SetBreakpoint(breakpoint); |
422 | return ret_sp; |
423 | } |
424 | |