| 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 = Status::FromErrorStringWithFormatv( |
| 99 | format: "BRN::CFSD: Unknown language: {0}." , 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 = Status::FromErrorString(str: "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 = Status::FromErrorString(str: "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 = Status::FromErrorString(str: "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 = Status::FromErrorString( |
| 140 | str: "BRN::CFSD: Missing symbol names mask entry." ); |
| 141 | return nullptr; |
| 142 | } |
| 143 | |
| 144 | size_t num_elem = names_array->GetSize(); |
| 145 | if (num_elem != names_mask_array->GetSize()) { |
| 146 | error = Status::FromErrorString( |
| 147 | str: "BRN::CFSD: names and names mask arrays have different sizes." ); |
| 148 | return nullptr; |
| 149 | } |
| 150 | |
| 151 | if (num_elem == 0) { |
| 152 | error = Status::FromErrorString( |
| 153 | str: "BRN::CFSD: no name entry in a breakpoint by name breakpoint." ); |
| 154 | return nullptr; |
| 155 | } |
| 156 | std::vector<std::string> names; |
| 157 | std::vector<FunctionNameType> name_masks; |
| 158 | for (size_t i = 0; i < num_elem; i++) { |
| 159 | std::optional<llvm::StringRef> maybe_name = |
| 160 | names_array->GetItemAtIndexAsString(idx: i); |
| 161 | if (!maybe_name) { |
| 162 | error = |
| 163 | Status::FromErrorString(str: "BRN::CFSD: name entry is not a string." ); |
| 164 | return nullptr; |
| 165 | } |
| 166 | auto maybe_fnt = names_mask_array->GetItemAtIndexAsInteger< |
| 167 | std::underlying_type<FunctionNameType>::type>(idx: i); |
| 168 | if (!maybe_fnt) { |
| 169 | error = Status::FromErrorString( |
| 170 | str: "BRN::CFSD: name mask entry is not an integer." ); |
| 171 | return nullptr; |
| 172 | } |
| 173 | names.push_back(x: std::string(*maybe_name)); |
| 174 | name_masks.push_back(x: static_cast<FunctionNameType>(*maybe_fnt)); |
| 175 | } |
| 176 | |
| 177 | std::shared_ptr<BreakpointResolverName> resolver_sp = |
| 178 | std::make_shared<BreakpointResolverName>( |
| 179 | args: nullptr, args: names[0].c_str(), args&: name_masks[0], args&: language, |
| 180 | args: Breakpoint::MatchType::Exact, args&: offset, args&: skip_prologue); |
| 181 | for (size_t i = 1; i < num_elem; i++) { |
| 182 | resolver_sp->AddNameLookup(name: ConstString(names[i]), name_type_mask: name_masks[i]); |
| 183 | } |
| 184 | return resolver_sp; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | StructuredData::ObjectSP BreakpointResolverName::SerializeToStructuredData() { |
| 189 | StructuredData::DictionarySP options_dict_sp( |
| 190 | new StructuredData::Dictionary()); |
| 191 | |
| 192 | if (m_regex.IsValid()) { |
| 193 | options_dict_sp->AddStringItem(key: GetKey(enum_value: OptionNames::RegexString), |
| 194 | value: m_regex.GetText()); |
| 195 | } else { |
| 196 | StructuredData::ArraySP names_sp(new StructuredData::Array()); |
| 197 | StructuredData::ArraySP name_masks_sp(new StructuredData::Array()); |
| 198 | for (auto lookup : m_lookups) { |
| 199 | names_sp->AddItem(item: StructuredData::StringSP( |
| 200 | new StructuredData::String(lookup.GetName().GetStringRef()))); |
| 201 | name_masks_sp->AddItem(item: StructuredData::UnsignedIntegerSP( |
| 202 | new StructuredData::UnsignedInteger(lookup.GetNameTypeMask()))); |
| 203 | } |
| 204 | options_dict_sp->AddItem(key: GetKey(enum_value: OptionNames::SymbolNameArray), value_sp: names_sp); |
| 205 | options_dict_sp->AddItem(key: GetKey(enum_value: OptionNames::NameMaskArray), value_sp: name_masks_sp); |
| 206 | } |
| 207 | if (m_language != eLanguageTypeUnknown) |
| 208 | options_dict_sp->AddStringItem( |
| 209 | key: GetKey(enum_value: OptionNames::LanguageName), |
| 210 | value: Language::GetNameForLanguageType(language: m_language)); |
| 211 | options_dict_sp->AddBooleanItem(key: GetKey(enum_value: OptionNames::SkipPrologue), |
| 212 | value: m_skip_prologue); |
| 213 | |
| 214 | return WrapOptionsDict(options_dict_sp); |
| 215 | } |
| 216 | |
| 217 | void BreakpointResolverName::AddNameLookup(ConstString name, |
| 218 | FunctionNameType name_type_mask) { |
| 219 | |
| 220 | Module::LookupInfo lookup(name, name_type_mask, m_language); |
| 221 | m_lookups.emplace_back(args&: lookup); |
| 222 | |
| 223 | auto add_variant_funcs = [&](Language *lang) { |
| 224 | for (Language::MethodNameVariant variant : |
| 225 | lang->GetMethodNameVariants(method_name: name)) { |
| 226 | // FIXME: Should we be adding variants that aren't of type Full? |
| 227 | if (variant.GetType() & lldb::eFunctionNameTypeFull) { |
| 228 | Module::LookupInfo variant_lookup(name, variant.GetType(), |
| 229 | lang->GetLanguageType()); |
| 230 | variant_lookup.SetLookupName(variant.GetName()); |
| 231 | m_lookups.emplace_back(args&: variant_lookup); |
| 232 | } |
| 233 | } |
| 234 | return true; |
| 235 | }; |
| 236 | |
| 237 | if (Language *lang = Language::FindPlugin(language: m_language)) { |
| 238 | add_variant_funcs(lang); |
| 239 | } else { |
| 240 | // Most likely m_language is eLanguageTypeUnknown. We check each language for |
| 241 | // possible variants or more qualified names and create lookups for those as |
| 242 | // well. |
| 243 | Language::ForEach(callback: add_variant_funcs); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | // FIXME: Right now we look at the module level, and call the module's |
| 248 | // "FindFunctions". |
| 249 | // Greg says he will add function tables, maybe at the CompileUnit level to |
| 250 | // accelerate function lookup. At that point, we should switch the depth to |
| 251 | // CompileUnit, and look in these tables. |
| 252 | |
| 253 | Searcher::CallbackReturn |
| 254 | BreakpointResolverName::SearchCallback(SearchFilter &filter, |
| 255 | SymbolContext &context, Address *addr) { |
| 256 | Log *log = GetLog(mask: LLDBLog::Breakpoints); |
| 257 | |
| 258 | if (m_class_name) { |
| 259 | if (log) |
| 260 | log->Warning(fmt: "Class/method function specification not supported yet.\n" ); |
| 261 | return Searcher::eCallbackReturnStop; |
| 262 | } |
| 263 | |
| 264 | SymbolContextList func_list; |
| 265 | bool filter_by_cu = |
| 266 | (filter.GetFilterRequiredItems() & eSymbolContextCompUnit) != 0; |
| 267 | bool filter_by_language = (m_language != eLanguageTypeUnknown); |
| 268 | |
| 269 | ModuleFunctionSearchOptions function_options; |
| 270 | function_options.include_symbols = !filter_by_cu; |
| 271 | function_options.include_inlines = true; |
| 272 | |
| 273 | switch (m_match_type) { |
| 274 | case Breakpoint::Exact: |
| 275 | if (context.module_sp) { |
| 276 | for (const auto &lookup : m_lookups) { |
| 277 | const size_t start_func_idx = func_list.GetSize(); |
| 278 | context.module_sp->FindFunctions(lookup_info: lookup, parent_decl_ctx: CompilerDeclContext(), |
| 279 | options: function_options, sc_list&: func_list); |
| 280 | |
| 281 | const size_t end_func_idx = func_list.GetSize(); |
| 282 | |
| 283 | if (start_func_idx < end_func_idx) |
| 284 | lookup.Prune(sc_list&: func_list, start_idx: start_func_idx); |
| 285 | } |
| 286 | } |
| 287 | break; |
| 288 | case Breakpoint::Regexp: |
| 289 | if (context.module_sp) { |
| 290 | context.module_sp->FindFunctions(regex: m_regex, options: function_options, sc_list&: func_list); |
| 291 | } |
| 292 | break; |
| 293 | case Breakpoint::Glob: |
| 294 | if (log) |
| 295 | log->Warning(fmt: "glob is not supported yet." ); |
| 296 | break; |
| 297 | } |
| 298 | |
| 299 | // If the filter specifies a Compilation Unit, remove the ones that don't |
| 300 | // pass at this point. |
| 301 | if (filter_by_cu || filter_by_language) { |
| 302 | uint32_t num_functions = func_list.GetSize(); |
| 303 | |
| 304 | for (size_t idx = 0; idx < num_functions; idx++) { |
| 305 | bool remove_it = false; |
| 306 | SymbolContext sc; |
| 307 | func_list.GetContextAtIndex(idx, sc); |
| 308 | if (filter_by_cu) { |
| 309 | if (!sc.comp_unit || !filter.CompUnitPasses(compUnit&: *sc.comp_unit)) |
| 310 | remove_it = true; |
| 311 | } |
| 312 | |
| 313 | if (filter_by_language) { |
| 314 | LanguageType sym_language = sc.GetLanguage(); |
| 315 | if ((Language::GetPrimaryLanguage(language: sym_language) != |
| 316 | Language::GetPrimaryLanguage(language: m_language)) && |
| 317 | (sym_language != eLanguageTypeUnknown)) { |
| 318 | remove_it = true; |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | if (remove_it) { |
| 323 | func_list.RemoveContextAtIndex(idx); |
| 324 | num_functions--; |
| 325 | idx--; |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | BreakpointSP breakpoint_sp = GetBreakpoint(); |
| 331 | Breakpoint &breakpoint = *breakpoint_sp; |
| 332 | Address break_addr; |
| 333 | |
| 334 | // Remove any duplicates between the function list and the symbol list |
| 335 | for (const SymbolContext &sc : func_list) { |
| 336 | bool is_reexported = false; |
| 337 | |
| 338 | if (sc.block && sc.block->GetInlinedFunctionInfo()) { |
| 339 | if (!sc.block->GetStartAddress(addr&: break_addr)) |
| 340 | break_addr.Clear(); |
| 341 | } else if (sc.function) { |
| 342 | break_addr = sc.function->GetAddress(); |
| 343 | if (m_skip_prologue && break_addr.IsValid()) { |
| 344 | const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize(); |
| 345 | if (prologue_byte_size) |
| 346 | break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); |
| 347 | } |
| 348 | } else if (sc.symbol) { |
| 349 | if (sc.symbol->GetType() == eSymbolTypeReExported) { |
| 350 | const Symbol *actual_symbol = |
| 351 | sc.symbol->ResolveReExportedSymbol(target&: breakpoint.GetTarget()); |
| 352 | if (actual_symbol) { |
| 353 | is_reexported = true; |
| 354 | break_addr = actual_symbol->GetAddress(); |
| 355 | } |
| 356 | } else { |
| 357 | break_addr = sc.symbol->GetAddress(); |
| 358 | } |
| 359 | |
| 360 | if (m_skip_prologue && break_addr.IsValid()) { |
| 361 | const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize(); |
| 362 | if (prologue_byte_size) |
| 363 | break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size); |
| 364 | else { |
| 365 | const Architecture *arch = |
| 366 | breakpoint.GetTarget().GetArchitecturePlugin(); |
| 367 | if (arch) |
| 368 | arch->AdjustBreakpointAddress(func: *sc.symbol, addr&: break_addr); |
| 369 | } |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | if (!break_addr.IsValid()) |
| 374 | continue; |
| 375 | |
| 376 | if (!filter.AddressPasses(addr&: break_addr)) |
| 377 | continue; |
| 378 | |
| 379 | bool new_location; |
| 380 | BreakpointLocationSP bp_loc_sp(AddLocation(loc_addr: break_addr, new_location: &new_location)); |
| 381 | bp_loc_sp->SetIsReExported(is_reexported); |
| 382 | if (bp_loc_sp && new_location && !breakpoint.IsInternal()) { |
| 383 | if (log) { |
| 384 | StreamString s; |
| 385 | bp_loc_sp->GetDescription(s: &s, level: lldb::eDescriptionLevelVerbose); |
| 386 | LLDB_LOGF(log, "Added location: %s\n" , s.GetData()); |
| 387 | } |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | return Searcher::eCallbackReturnContinue; |
| 392 | } |
| 393 | |
| 394 | lldb::SearchDepth BreakpointResolverName::GetDepth() { |
| 395 | return lldb::eSearchDepthModule; |
| 396 | } |
| 397 | |
| 398 | void BreakpointResolverName::GetDescription(Stream *s) { |
| 399 | if (m_match_type == Breakpoint::Regexp) |
| 400 | s->Printf(format: "regex = '%s'" , m_regex.GetText().str().c_str()); |
| 401 | else { |
| 402 | size_t num_names = m_lookups.size(); |
| 403 | if (num_names == 1) |
| 404 | s->Printf(format: "name = '%s'" , m_lookups[0].GetName().GetCString()); |
| 405 | else { |
| 406 | s->Printf(format: "names = {" ); |
| 407 | for (size_t i = 0; i < num_names; i++) { |
| 408 | s->Printf(format: "%s'%s'" , (i == 0 ? "" : ", " ), |
| 409 | m_lookups[i].GetName().GetCString()); |
| 410 | } |
| 411 | s->Printf(format: "}" ); |
| 412 | } |
| 413 | } |
| 414 | if (m_language != eLanguageTypeUnknown) { |
| 415 | s->Printf(format: ", language = %s" , Language::GetNameForLanguageType(language: m_language)); |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | void BreakpointResolverName::Dump(Stream *s) const {} |
| 420 | |
| 421 | lldb::BreakpointResolverSP |
| 422 | BreakpointResolverName::CopyForBreakpoint(BreakpointSP &breakpoint) { |
| 423 | lldb::BreakpointResolverSP ret_sp(new BreakpointResolverName(*this)); |
| 424 | ret_sp->SetBreakpoint(breakpoint); |
| 425 | return ret_sp; |
| 426 | } |
| 427 | |