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