1 //===-- ItaniumABILanguageRuntime.cpp --------------------------------------*- 2 //C++ -*-===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "ItaniumABILanguageRuntime.h" 11 12 #include "lldb/Breakpoint/BreakpointLocation.h" 13 #include "lldb/Core/Mangled.h" 14 #include "lldb/Core/Module.h" 15 #include "lldb/Core/PluginManager.h" 16 #include "lldb/Core/ValueObject.h" 17 #include "lldb/Core/ValueObjectMemory.h" 18 #include "lldb/DataFormatters/FormattersHelpers.h" 19 #include "lldb/Expression/DiagnosticManager.h" 20 #include "lldb/Expression/FunctionCaller.h" 21 #include "lldb/Interpreter/CommandObject.h" 22 #include "lldb/Interpreter/CommandObjectMultiword.h" 23 #include "lldb/Interpreter/CommandReturnObject.h" 24 #include "lldb/Symbol/ClangASTContext.h" 25 #include "lldb/Symbol/Symbol.h" 26 #include "lldb/Symbol/SymbolFile.h" 27 #include "lldb/Symbol/TypeList.h" 28 #include "lldb/Target/Process.h" 29 #include "lldb/Target/RegisterContext.h" 30 #include "lldb/Target/SectionLoadList.h" 31 #include "lldb/Target/StopInfo.h" 32 #include "lldb/Target/Target.h" 33 #include "lldb/Target/Thread.h" 34 #include "lldb/Utility/ConstString.h" 35 #include "lldb/Utility/Log.h" 36 #include "lldb/Utility/Scalar.h" 37 #include "lldb/Utility/Status.h" 38 39 #include <vector> 40 41 using namespace lldb; 42 using namespace lldb_private; 43 44 static const char *vtable_demangled_prefix = "vtable for "; 45 46 char ItaniumABILanguageRuntime::ID = 0; 47 48 bool ItaniumABILanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) { 49 const bool check_cxx = true; 50 const bool check_objc = false; 51 return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, check_cxx, 52 check_objc); 53 } 54 55 TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfoFromVTableAddress( 56 ValueObject &in_value, lldb::addr_t original_ptr, 57 lldb::addr_t vtable_load_addr) { 58 if (m_process && vtable_load_addr != LLDB_INVALID_ADDRESS) { 59 // Find the symbol that contains the "vtable_load_addr" address 60 Address vtable_addr; 61 Target &target = m_process->GetTarget(); 62 if (!target.GetSectionLoadList().IsEmpty()) { 63 if (target.GetSectionLoadList().ResolveLoadAddress(vtable_load_addr, 64 vtable_addr)) { 65 // See if we have cached info for this type already 66 TypeAndOrName type_info = GetDynamicTypeInfo(vtable_addr); 67 if (type_info) 68 return type_info; 69 70 SymbolContext sc; 71 target.GetImages().ResolveSymbolContextForAddress( 72 vtable_addr, eSymbolContextSymbol, sc); 73 Symbol *symbol = sc.symbol; 74 if (symbol != nullptr) { 75 const char *name = 76 symbol->GetMangled() 77 .GetDemangledName(lldb::eLanguageTypeC_plus_plus) 78 .AsCString(); 79 if (name && strstr(name, vtable_demangled_prefix) == name) { 80 Log *log( 81 lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); 82 LLDB_LOGF(log, 83 "0x%16.16" PRIx64 84 ": static-type = '%s' has vtable symbol '%s'\n", 85 original_ptr, in_value.GetTypeName().GetCString(), name); 86 // We are a C++ class, that's good. Get the class name and look it 87 // up: 88 const char *class_name = name + strlen(vtable_demangled_prefix); 89 // We know the class name is absolute, so tell FindTypes that by 90 // prefixing it with the root namespace: 91 std::string lookup_name("::"); 92 lookup_name.append(class_name); 93 94 type_info.SetName(class_name); 95 const bool exact_match = true; 96 TypeList class_types; 97 98 uint32_t num_matches = 0; 99 // First look in the module that the vtable symbol came from and 100 // look for a single exact match. 101 llvm::DenseSet<SymbolFile *> searched_symbol_files; 102 if (sc.module_sp) { 103 num_matches = sc.module_sp->FindTypes( 104 ConstString(lookup_name), exact_match, 1, 105 searched_symbol_files, class_types); 106 } 107 108 // If we didn't find a symbol, then move on to the entire module 109 // list in the target and get as many unique matches as possible 110 if (num_matches == 0) { 111 num_matches = target.GetImages().FindTypes( 112 nullptr, ConstString(lookup_name), exact_match, UINT32_MAX, 113 searched_symbol_files, class_types); 114 } 115 116 lldb::TypeSP type_sp; 117 if (num_matches == 0) { 118 LLDB_LOGF(log, "0x%16.16" PRIx64 ": is not dynamic\n", 119 original_ptr); 120 return TypeAndOrName(); 121 } 122 if (num_matches == 1) { 123 type_sp = class_types.GetTypeAtIndex(0); 124 if (type_sp) { 125 if (ClangASTContext::IsCXXClassType( 126 type_sp->GetForwardCompilerType())) { 127 LLDB_LOGF( 128 log, 129 "0x%16.16" PRIx64 130 ": static-type = '%s' has dynamic type: uid={0x%" PRIx64 131 "}, type-name='%s'\n", 132 original_ptr, in_value.GetTypeName().AsCString(), 133 type_sp->GetID(), type_sp->GetName().GetCString()); 134 type_info.SetTypeSP(type_sp); 135 } 136 } 137 } else if (num_matches > 1) { 138 size_t i; 139 if (log) { 140 for (i = 0; i < num_matches; i++) { 141 type_sp = class_types.GetTypeAtIndex(i); 142 if (type_sp) { 143 LLDB_LOGF( 144 log, 145 "0x%16.16" PRIx64 146 ": static-type = '%s' has multiple matching dynamic " 147 "types: uid={0x%" PRIx64 "}, type-name='%s'\n", 148 original_ptr, in_value.GetTypeName().AsCString(), 149 type_sp->GetID(), type_sp->GetName().GetCString()); 150 } 151 } 152 } 153 154 for (i = 0; i < num_matches; i++) { 155 type_sp = class_types.GetTypeAtIndex(i); 156 if (type_sp) { 157 if (ClangASTContext::IsCXXClassType( 158 type_sp->GetForwardCompilerType())) { 159 LLDB_LOGF( 160 log, 161 "0x%16.16" PRIx64 ": static-type = '%s' has multiple " 162 "matching dynamic types, picking " 163 "this one: uid={0x%" PRIx64 "}, type-name='%s'\n", 164 original_ptr, in_value.GetTypeName().AsCString(), 165 type_sp->GetID(), type_sp->GetName().GetCString()); 166 type_info.SetTypeSP(type_sp); 167 } 168 } 169 } 170 171 if (log && i == num_matches) { 172 LLDB_LOGF(log, 173 "0x%16.16" PRIx64 174 ": static-type = '%s' has multiple matching dynamic " 175 "types, didn't find a C++ match\n", 176 original_ptr, in_value.GetTypeName().AsCString()); 177 } 178 } 179 if (type_info) 180 SetDynamicTypeInfo(vtable_addr, type_info); 181 return type_info; 182 } 183 } 184 } 185 } 186 } 187 return TypeAndOrName(); 188 } 189 190 bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( 191 ValueObject &in_value, lldb::DynamicValueType use_dynamic, 192 TypeAndOrName &class_type_or_name, Address &dynamic_address, 193 Value::ValueType &value_type) { 194 // For Itanium, if the type has a vtable pointer in the object, it will be at 195 // offset 0 in the object. That will point to the "address point" within the 196 // vtable (not the beginning of the vtable.) We can then look up the symbol 197 // containing this "address point" and that symbol's name demangled will 198 // contain the full class name. The second pointer above the "address point" 199 // is the "offset_to_top". We'll use that to get the start of the value 200 // object which holds the dynamic type. 201 // 202 203 class_type_or_name.Clear(); 204 value_type = Value::ValueType::eValueTypeScalar; 205 206 // Only a pointer or reference type can have a different dynamic and static 207 // type: 208 if (!CouldHaveDynamicValue(in_value)) 209 return false; 210 211 // First job, pull out the address at 0 offset from the object. 212 AddressType address_type; 213 lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type); 214 if (original_ptr == LLDB_INVALID_ADDRESS) 215 return false; 216 217 ExecutionContext exe_ctx(in_value.GetExecutionContextRef()); 218 219 Process *process = exe_ctx.GetProcessPtr(); 220 221 if (process == nullptr) 222 return false; 223 224 Status error; 225 const lldb::addr_t vtable_address_point = 226 process->ReadPointerFromMemory(original_ptr, error); 227 228 if (!error.Success() || vtable_address_point == LLDB_INVALID_ADDRESS) 229 return false; 230 231 class_type_or_name = GetTypeInfoFromVTableAddress(in_value, original_ptr, 232 vtable_address_point); 233 234 if (!class_type_or_name) 235 return false; 236 237 CompilerType type = class_type_or_name.GetCompilerType(); 238 // There can only be one type with a given name, so we've just found 239 // duplicate definitions, and this one will do as well as any other. We 240 // don't consider something to have a dynamic type if it is the same as 241 // the static type. So compare against the value we were handed. 242 if (!type) 243 return true; 244 245 if (ClangASTContext::AreTypesSame(in_value.GetCompilerType(), type)) { 246 // The dynamic type we found was the same type, so we don't have a 247 // dynamic type here... 248 return false; 249 } 250 251 // The offset_to_top is two pointers above the vtable pointer. 252 const uint32_t addr_byte_size = process->GetAddressByteSize(); 253 const lldb::addr_t offset_to_top_location = 254 vtable_address_point - 2 * addr_byte_size; 255 // Watch for underflow, offset_to_top_location should be less than 256 // vtable_address_point 257 if (offset_to_top_location >= vtable_address_point) 258 return false; 259 const int64_t offset_to_top = process->ReadSignedIntegerFromMemory( 260 offset_to_top_location, addr_byte_size, INT64_MIN, error); 261 262 if (offset_to_top == INT64_MIN) 263 return false; 264 // So the dynamic type is a value that starts at offset_to_top above 265 // the original address. 266 lldb::addr_t dynamic_addr = original_ptr + offset_to_top; 267 if (!process->GetTarget().GetSectionLoadList().ResolveLoadAddress( 268 dynamic_addr, dynamic_address)) { 269 dynamic_address.SetRawAddress(dynamic_addr); 270 } 271 return true; 272 } 273 274 TypeAndOrName ItaniumABILanguageRuntime::FixUpDynamicType( 275 const TypeAndOrName &type_and_or_name, ValueObject &static_value) { 276 CompilerType static_type(static_value.GetCompilerType()); 277 Flags static_type_flags(static_type.GetTypeInfo()); 278 279 TypeAndOrName ret(type_and_or_name); 280 if (type_and_or_name.HasType()) { 281 // The type will always be the type of the dynamic object. If our parent's 282 // type was a pointer, then our type should be a pointer to the type of the 283 // dynamic object. If a reference, then the original type should be 284 // okay... 285 CompilerType orig_type = type_and_or_name.GetCompilerType(); 286 CompilerType corrected_type = orig_type; 287 if (static_type_flags.AllSet(eTypeIsPointer)) 288 corrected_type = orig_type.GetPointerType(); 289 else if (static_type_flags.AllSet(eTypeIsReference)) 290 corrected_type = orig_type.GetLValueReferenceType(); 291 ret.SetCompilerType(corrected_type); 292 } else { 293 // If we are here we need to adjust our dynamic type name to include the 294 // correct & or * symbol 295 std::string corrected_name(type_and_or_name.GetName().GetCString()); 296 if (static_type_flags.AllSet(eTypeIsPointer)) 297 corrected_name.append(" *"); 298 else if (static_type_flags.AllSet(eTypeIsReference)) 299 corrected_name.append(" &"); 300 // the parent type should be a correctly pointer'ed or referenc'ed type 301 ret.SetCompilerType(static_type); 302 ret.SetName(corrected_name.c_str()); 303 } 304 return ret; 305 } 306 307 // Static Functions 308 LanguageRuntime * 309 ItaniumABILanguageRuntime::CreateInstance(Process *process, 310 lldb::LanguageType language) { 311 // FIXME: We have to check the process and make sure we actually know that 312 // this process supports 313 // the Itanium ABI. 314 if (language == eLanguageTypeC_plus_plus || 315 language == eLanguageTypeC_plus_plus_03 || 316 language == eLanguageTypeC_plus_plus_11 || 317 language == eLanguageTypeC_plus_plus_14) 318 return new ItaniumABILanguageRuntime(process); 319 else 320 return nullptr; 321 } 322 323 class CommandObjectMultiwordItaniumABI_Demangle : public CommandObjectParsed { 324 public: 325 CommandObjectMultiwordItaniumABI_Demangle(CommandInterpreter &interpreter) 326 : CommandObjectParsed(interpreter, "demangle", 327 "Demangle a C++ mangled name.", 328 "language cplusplus demangle") { 329 CommandArgumentEntry arg; 330 CommandArgumentData index_arg; 331 332 // Define the first (and only) variant of this arg. 333 index_arg.arg_type = eArgTypeSymbol; 334 index_arg.arg_repetition = eArgRepeatPlus; 335 336 // There is only one variant this argument could be; put it into the 337 // argument entry. 338 arg.push_back(index_arg); 339 340 // Push the data for the first argument into the m_arguments vector. 341 m_arguments.push_back(arg); 342 } 343 344 ~CommandObjectMultiwordItaniumABI_Demangle() override = default; 345 346 protected: 347 bool DoExecute(Args &command, CommandReturnObject &result) override { 348 bool demangled_any = false; 349 bool error_any = false; 350 for (auto &entry : command.entries()) { 351 if (entry.ref.empty()) 352 continue; 353 354 // the actual Mangled class should be strict about this, but on the 355 // command line if you're copying mangled names out of 'nm' on Darwin, 356 // they will come out with an extra underscore - be willing to strip this 357 // on behalf of the user. This is the moral equivalent of the -_/-n 358 // options to c++filt 359 auto name = entry.ref; 360 if (name.startswith("__Z")) 361 name = name.drop_front(); 362 363 Mangled mangled(name, true); 364 if (mangled.GuessLanguage() == lldb::eLanguageTypeC_plus_plus) { 365 ConstString demangled( 366 mangled.GetDisplayDemangledName(lldb::eLanguageTypeC_plus_plus)); 367 demangled_any = true; 368 result.AppendMessageWithFormat("%s ---> %s\n", entry.ref.str().c_str(), 369 demangled.GetCString()); 370 } else { 371 error_any = true; 372 result.AppendErrorWithFormat("%s is not a valid C++ mangled name\n", 373 entry.ref.str().c_str()); 374 } 375 } 376 377 result.SetStatus( 378 error_any ? lldb::eReturnStatusFailed 379 : (demangled_any ? lldb::eReturnStatusSuccessFinishResult 380 : lldb::eReturnStatusSuccessFinishNoResult)); 381 return result.Succeeded(); 382 } 383 }; 384 385 class CommandObjectMultiwordItaniumABI : public CommandObjectMultiword { 386 public: 387 CommandObjectMultiwordItaniumABI(CommandInterpreter &interpreter) 388 : CommandObjectMultiword( 389 interpreter, "cplusplus", 390 "Commands for operating on the C++ language runtime.", 391 "cplusplus <subcommand> [<subcommand-options>]") { 392 LoadSubCommand( 393 "demangle", 394 CommandObjectSP( 395 new CommandObjectMultiwordItaniumABI_Demangle(interpreter))); 396 } 397 398 ~CommandObjectMultiwordItaniumABI() override = default; 399 }; 400 401 void ItaniumABILanguageRuntime::Initialize() { 402 PluginManager::RegisterPlugin( 403 GetPluginNameStatic(), "Itanium ABI for the C++ language", CreateInstance, 404 [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP { 405 return CommandObjectSP( 406 new CommandObjectMultiwordItaniumABI(interpreter)); 407 }); 408 } 409 410 void ItaniumABILanguageRuntime::Terminate() { 411 PluginManager::UnregisterPlugin(CreateInstance); 412 } 413 414 lldb_private::ConstString ItaniumABILanguageRuntime::GetPluginNameStatic() { 415 static ConstString g_name("itanium"); 416 return g_name; 417 } 418 419 // PluginInterface protocol 420 lldb_private::ConstString ItaniumABILanguageRuntime::GetPluginName() { 421 return GetPluginNameStatic(); 422 } 423 424 uint32_t ItaniumABILanguageRuntime::GetPluginVersion() { return 1; } 425 426 BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver( 427 Breakpoint *bkpt, bool catch_bp, bool throw_bp) { 428 return CreateExceptionResolver(bkpt, catch_bp, throw_bp, false); 429 } 430 431 BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver( 432 Breakpoint *bkpt, bool catch_bp, bool throw_bp, bool for_expressions) { 433 // One complication here is that most users DON'T want to stop at 434 // __cxa_allocate_expression, but until we can do anything better with 435 // predicting unwinding the expression parser does. So we have two forms of 436 // the exception breakpoints, one for expressions that leaves out 437 // __cxa_allocate_exception, and one that includes it. The 438 // SetExceptionBreakpoints does the latter, the CreateExceptionBreakpoint in 439 // the runtime the former. 440 static const char *g_catch_name = "__cxa_begin_catch"; 441 static const char *g_throw_name1 = "__cxa_throw"; 442 static const char *g_throw_name2 = "__cxa_rethrow"; 443 static const char *g_exception_throw_name = "__cxa_allocate_exception"; 444 std::vector<const char *> exception_names; 445 exception_names.reserve(4); 446 if (catch_bp) 447 exception_names.push_back(g_catch_name); 448 449 if (throw_bp) { 450 exception_names.push_back(g_throw_name1); 451 exception_names.push_back(g_throw_name2); 452 } 453 454 if (for_expressions) 455 exception_names.push_back(g_exception_throw_name); 456 457 BreakpointResolverSP resolver_sp(new BreakpointResolverName( 458 bkpt, exception_names.data(), exception_names.size(), 459 eFunctionNameTypeBase, eLanguageTypeUnknown, 0, eLazyBoolNo)); 460 461 return resolver_sp; 462 } 463 464 lldb::SearchFilterSP ItaniumABILanguageRuntime::CreateExceptionSearchFilter() { 465 Target &target = m_process->GetTarget(); 466 467 FileSpecList filter_modules; 468 if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) { 469 // Limit the number of modules that are searched for these breakpoints for 470 // Apple binaries. 471 filter_modules.EmplaceBack("libc++abi.dylib"); 472 filter_modules.EmplaceBack("libSystem.B.dylib"); 473 } 474 return target.GetSearchFilterForModuleList(&filter_modules); 475 } 476 477 lldb::BreakpointSP ItaniumABILanguageRuntime::CreateExceptionBreakpoint( 478 bool catch_bp, bool throw_bp, bool for_expressions, bool is_internal) { 479 Target &target = m_process->GetTarget(); 480 FileSpecList filter_modules; 481 BreakpointResolverSP exception_resolver_sp = 482 CreateExceptionResolver(nullptr, catch_bp, throw_bp, for_expressions); 483 SearchFilterSP filter_sp(CreateExceptionSearchFilter()); 484 const bool hardware = false; 485 const bool resolve_indirect_functions = false; 486 return target.CreateBreakpoint(filter_sp, exception_resolver_sp, is_internal, 487 hardware, resolve_indirect_functions); 488 } 489 490 void ItaniumABILanguageRuntime::SetExceptionBreakpoints() { 491 if (!m_process) 492 return; 493 494 const bool catch_bp = false; 495 const bool throw_bp = true; 496 const bool is_internal = true; 497 const bool for_expressions = true; 498 499 // For the exception breakpoints set by the Expression parser, we'll be a 500 // little more aggressive and stop at exception allocation as well. 501 502 if (m_cxx_exception_bp_sp) { 503 m_cxx_exception_bp_sp->SetEnabled(true); 504 } else { 505 m_cxx_exception_bp_sp = CreateExceptionBreakpoint( 506 catch_bp, throw_bp, for_expressions, is_internal); 507 if (m_cxx_exception_bp_sp) 508 m_cxx_exception_bp_sp->SetBreakpointKind("c++ exception"); 509 } 510 } 511 512 void ItaniumABILanguageRuntime::ClearExceptionBreakpoints() { 513 if (!m_process) 514 return; 515 516 if (m_cxx_exception_bp_sp) { 517 m_cxx_exception_bp_sp->SetEnabled(false); 518 } 519 } 520 521 bool ItaniumABILanguageRuntime::ExceptionBreakpointsAreSet() { 522 return m_cxx_exception_bp_sp && m_cxx_exception_bp_sp->IsEnabled(); 523 } 524 525 bool ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop( 526 lldb::StopInfoSP stop_reason) { 527 if (!m_process) 528 return false; 529 530 if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint) 531 return false; 532 533 uint64_t break_site_id = stop_reason->GetValue(); 534 return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint( 535 break_site_id, m_cxx_exception_bp_sp->GetID()); 536 } 537 538 ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread( 539 ThreadSP thread_sp) { 540 if (!thread_sp->SafeToCallFunctions()) 541 return {}; 542 543 ClangASTContext *clang_ast_context = 544 m_process->GetTarget().GetScratchClangASTContext(); 545 CompilerType voidstar = 546 clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); 547 548 DiagnosticManager diagnostics; 549 ExecutionContext exe_ctx; 550 EvaluateExpressionOptions options; 551 552 options.SetUnwindOnError(true); 553 options.SetIgnoreBreakpoints(true); 554 options.SetStopOthers(true); 555 options.SetTimeout(m_process->GetUtilityExpressionTimeout()); 556 options.SetTryAllThreads(false); 557 thread_sp->CalculateExecutionContext(exe_ctx); 558 559 const ModuleList &modules = m_process->GetTarget().GetImages(); 560 SymbolContextList contexts; 561 SymbolContext context; 562 563 modules.FindSymbolsWithNameAndType( 564 ConstString("__cxa_current_exception_type"), eSymbolTypeCode, contexts); 565 contexts.GetContextAtIndex(0, context); 566 Address addr = context.symbol->GetAddress(); 567 568 Status error; 569 FunctionCaller *function_caller = 570 m_process->GetTarget().GetFunctionCallerForLanguage( 571 eLanguageTypeC, voidstar, addr, ValueList(), "caller", error); 572 573 ExpressionResults func_call_ret; 574 Value results; 575 func_call_ret = function_caller->ExecuteFunction(exe_ctx, nullptr, options, 576 diagnostics, results); 577 if (func_call_ret != eExpressionCompleted || !error.Success()) { 578 return ValueObjectSP(); 579 } 580 581 size_t ptr_size = m_process->GetAddressByteSize(); 582 addr_t result_ptr = results.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); 583 addr_t exception_addr = 584 m_process->ReadPointerFromMemory(result_ptr - ptr_size, error); 585 586 if (!error.Success()) { 587 return ValueObjectSP(); 588 } 589 590 lldb_private::formatters::InferiorSizedWord exception_isw(exception_addr, 591 *m_process); 592 ValueObjectSP exception = ValueObject::CreateValueObjectFromData( 593 "exception", exception_isw.GetAsData(m_process->GetByteOrder()), exe_ctx, 594 voidstar); 595 exception = exception->GetDynamicValue(eDynamicDontRunTarget); 596 597 return exception; 598 } 599 600 TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo( 601 const lldb_private::Address &vtable_addr) { 602 std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex); 603 DynamicTypeCache::const_iterator pos = m_dynamic_type_map.find(vtable_addr); 604 if (pos == m_dynamic_type_map.end()) 605 return TypeAndOrName(); 606 else 607 return pos->second; 608 } 609 610 void ItaniumABILanguageRuntime::SetDynamicTypeInfo( 611 const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info) { 612 std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex); 613 m_dynamic_type_map[vtable_addr] = type_info; 614 } 615