diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index fd084382f7493dbd70bc3586fbd0ec57b926a46b..0346d92720a7f1150f751af34097080d055151f1 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -138,11 +138,7 @@ public: } void - SetUpdated () - { - m_first_update = false; - m_needs_update = false; - } + SetUpdated (); bool NeedsUpdating() diff --git a/lldb/include/lldb/Symbol/Type.h b/lldb/include/lldb/Symbol/Type.h index 316b61644080378b452633170c03261ddbc2040d..7dac894318ac48c6a9a62a2018fd9fee53e2f468 100644 --- a/lldb/include/lldb/Symbol/Type.h +++ b/lldb/include/lldb/Symbol/Type.h @@ -12,6 +12,7 @@ #include "lldb/lldb-private.h" #include "lldb/Core/ClangForward.h" +#include "lldb/Core/ConstString.h" #include "lldb/Core/UserID.h" #include "lldb/Symbol/Declaration.h" #include @@ -250,6 +251,48 @@ protected: ResolveClangType (ResolveState clang_type_resolve_state); }; + +/// +/// Sometimes you can find the name of the type corresponding to an object, but we don't have debug +/// information for it. If that is the case, you can return one of these objects, and then if it +/// has a full type, you can use that, but if not at least you can print the name for informational +/// purposes. +/// + +class TypeAndOrName +{ +public: + TypeAndOrName (); + TypeAndOrName (lldb::TypeSP &type_sp); + TypeAndOrName (const char *type_str); + TypeAndOrName (const TypeAndOrName &rhs); + TypeAndOrName (ConstString &type_const_string); + + TypeAndOrName & + operator= (const TypeAndOrName &rhs); + + ConstString GetName () const; + lldb::TypeSP GetTypeSP () const { + return m_type_sp; + }; + + void + SetName (ConstString &type_name_const_str); + + void + SetName (const char *type_name_str); + + void + SetTypeSP (lldb::TypeSP type_sp); + + bool + IsEmpty (); + +private: + lldb::TypeSP m_type_sp; + ConstString m_type_name; +}; + } // namespace lldb_private #endif // liblldb_Type_h_ diff --git a/lldb/include/lldb/Target/LanguageRuntime.h b/lldb/include/lldb/Target/LanguageRuntime.h index 6005e34e6d344774676bfc043ad9df4b6f666672..3be0409f498969d55136e2a575087f217abd1a06 100644 --- a/lldb/include/lldb/Target/LanguageRuntime.h +++ b/lldb/include/lldb/Target/LanguageRuntime.h @@ -43,7 +43,7 @@ public: GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope) = 0; virtual bool - GetDynamicValue (ValueObject &in_value, lldb::TypeSP &type_sp, Address &address) = 0; + GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &address) = 0; // This should be a fast test to determine whether it is likely that this value would // have a dynamic type. diff --git a/lldb/include/lldb/Target/ObjCLanguageRuntime.h b/lldb/include/lldb/Target/ObjCLanguageRuntime.h index e2fe9e1d238da030325a1bfc701a7b906649cfd9..b00e436e7028406d394dba9bf96631cd57e8a6b1 100644 --- a/lldb/include/lldb/Target/ObjCLanguageRuntime.h +++ b/lldb/include/lldb/Target/ObjCLanguageRuntime.h @@ -55,6 +55,15 @@ public: void AddToMethodCache (lldb::addr_t class_addr, lldb::addr_t sel, lldb::addr_t impl_addr); + TypeAndOrName + LookupInClassNameCache (lldb::addr_t class_addr); + + void + AddToClassNameCache (lldb::addr_t class_addr, const char *name, lldb::TypeSP type_sp); + + void + AddToClassNameCache (lldb::addr_t class_addr, const TypeAndOrName &class_or_type_name); + virtual ClangUtilityFunction * CreateObjectChecker (const char *) = 0; @@ -117,7 +126,10 @@ private: }; typedef std::map MsgImplMap; - MsgImplMap m_impl_cache; + MsgImplMap m_impl_cache; + + typedef std::map ClassNameMap; + ClassNameMap m_class_name_cache; DISALLOW_COPY_AND_ASSIGN (ObjCLanguageRuntime); }; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 607f081e3f212e98d171dcb994ff86165dd3a31c..3ab513f9ce619583ad47f8c09981b40792dc3b55 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -155,6 +155,7 @@ class ThreadPlanTracer; class ThreadSpec; class TimeValue; class Type; +class TypeAndOrName; class TypeList; class UUID; class Unwind; diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index 6ad6bb99b9d3da6d5bbe3bd991505832b89f8242..f499fae145f2c8bb959147b183c65d9680cce9ea 100644 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -357,7 +357,7 @@ SBValue::GetChildAtIndex (uint32_t idx, bool use_dynamic_value) { if (child_sp) { - lldb::ValueObjectSP dynamic_sp = child_sp->GetDynamicValue(true); + lldb::ValueObjectSP dynamic_sp = child_sp->GetDynamicValue (true); if (dynamic_sp) child_sp = dynamic_sp; } @@ -410,7 +410,7 @@ SBValue::GetChildMemberWithName (const char *name, bool use_dynamic_value) { if (child_sp) { - lldb::ValueObjectSP dynamic_sp = child_sp->GetDynamicValue(true); + lldb::ValueObjectSP dynamic_sp = child_sp->GetDynamicValue (true); if (dynamic_sp) child_sp = dynamic_sp; } diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 1f443e81502b96bafc6726b804ea8a94e56b3b1a..521d3eb58801bac65665101e43c14e0b791c2e21 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -1644,6 +1644,16 @@ ValueObject::EvaluationPoint::SyncWithProcessState() return true; } +void +ValueObject::EvaluationPoint::SetUpdated () +{ + m_first_update = false; + m_needs_update = false; + if (m_process_sp) + m_stop_id = m_process_sp->GetStopID(); +} + + bool ValueObject::EvaluationPoint::SetContext (ExecutionContextScope *exe_scope) { diff --git a/lldb/source/Core/ValueObjectDynamicValue.cpp b/lldb/source/Core/ValueObjectDynamicValue.cpp index 17eb90a335f68c9f2142285ffe0a28b0eab6f8e1..b43c6519baf191a8a5ff2eb1474a265fc06590e4 100644 --- a/lldb/source/Core/ValueObjectDynamicValue.cpp +++ b/lldb/source/Core/ValueObjectDynamicValue.cpp @@ -135,7 +135,7 @@ ValueObjectDynamicValue::UpdateValue () if (!process) return false; - lldb::TypeSP dynamic_type_sp; + TypeAndOrName class_type_or_name; Address dynamic_address; bool found_dynamic_type = false; @@ -144,22 +144,29 @@ ValueObjectDynamicValue::UpdateValue () { LanguageRuntime *runtime = process->GetLanguageRuntime (known_type); if (runtime) - found_dynamic_type = runtime->GetDynamicValue(*m_parent, dynamic_type_sp, dynamic_address); + found_dynamic_type = runtime->GetDynamicTypeAndAddress (*m_parent, class_type_or_name, dynamic_address); } else { LanguageRuntime *cpp_runtime = process->GetLanguageRuntime (lldb::eLanguageTypeC_plus_plus); if (cpp_runtime) - found_dynamic_type = cpp_runtime->GetDynamicValue(*m_parent, dynamic_type_sp, dynamic_address); + found_dynamic_type = cpp_runtime->GetDynamicTypeAndAddress (*m_parent, class_type_or_name, dynamic_address); if (!found_dynamic_type) { LanguageRuntime *objc_runtime = process->GetLanguageRuntime (lldb::eLanguageTypeObjC); if (objc_runtime) - found_dynamic_type = cpp_runtime->GetDynamicValue(*m_parent, dynamic_type_sp, dynamic_address); + found_dynamic_type = cpp_runtime->GetDynamicTypeAndAddress (*m_parent, class_type_or_name, dynamic_address); } } + lldb::TypeSP dynamic_type_sp = class_type_or_name.GetTypeSP(); + + // Getting the dynamic value may have run the program a bit, and so marked us as needing updating, but we really + // don't... + + m_update_point.SetUpdated(); + // If we don't have a dynamic type, then make ourselves just a echo of our parent. // Or we could return false, and make ourselves an echo of our parent? if (!found_dynamic_type) diff --git a/lldb/source/Expression/ClangExpressionDeclMap.cpp b/lldb/source/Expression/ClangExpressionDeclMap.cpp index 60a37bc59490e8933041e9f5bbc9411d5bfd8e98..678c175b22b594bed23037a93ba9aeca8095dafb 100644 --- a/lldb/source/Expression/ClangExpressionDeclMap.cpp +++ b/lldb/source/Expression/ClangExpressionDeclMap.cpp @@ -73,7 +73,8 @@ ClangExpressionDeclMap::WillParse(ExecutionContext &exe_ctx) m_parser_vars->m_sym_ctx = exe_ctx.frame->GetSymbolContext(lldb::eSymbolContextEverything); else if (exe_ctx.thread) m_parser_vars->m_sym_ctx = exe_ctx.thread->GetStackFrameAtIndex(0)->GetSymbolContext(lldb::eSymbolContextEverything); - + else if (exe_ctx.process) + m_parser_vars->m_sym_ctx = SymbolContext(exe_ctx.target->GetSP(), ModuleSP()); if (exe_ctx.target) m_parser_vars->m_persistent_vars = &exe_ctx.target->GetPersistentVariables(); } diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp index fb4c98dc44197fdbe94d0e249e8f968153465ce9..20b608eb401de8c895b0d136dd86d01e55f6fb53 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp @@ -41,7 +41,7 @@ ItaniumABILanguageRuntime::CouldHaveDynamicValue (ValueObject &in_value) } bool -ItaniumABILanguageRuntime::GetDynamicValue (ValueObject &in_value, lldb::TypeSP &dynamic_type_sp, Address &dynamic_address) +ItaniumABILanguageRuntime::GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &dynamic_address) { // For Itanium, if the type has a vtable pointer in the object, it will be at offset 0 // in the object. That will point to the "address point" within the vtable (not the beginning of the @@ -104,6 +104,7 @@ ItaniumABILanguageRuntime::GetDynamicValue (ValueObject &in_value, lldb::TypeSP { // We are a C++ class, that's good. Get the class name and look it up: const char *class_name = name + strlen(vtable_demangled_prefix); + class_type_or_name.SetName (class_name); TypeList class_types; uint32_t num_matches = target->GetImages().FindTypes (sc, ConstString(class_name), @@ -112,14 +113,40 @@ ItaniumABILanguageRuntime::GetDynamicValue (ValueObject &in_value, lldb::TypeSP class_types); if (num_matches == 1) { - dynamic_type_sp = class_types.GetTypeAtIndex(0); + class_type_or_name.SetTypeSP(class_types.GetTypeAtIndex(0)); } else if (num_matches > 1) { - // How to sort out which of the type matches to pick? + for (size_t i = 0; i < num_matches; i++) + { + lldb::TypeSP this_type(class_types.GetTypeAtIndex(i)); + if (this_type) + { + if (ClangASTContext::IsCXXClassType(this_type->GetClangFullType())) + { + // There can only be one type with a given name, + // so we've just found duplicate definitions, and this + // one will do as well as any other. + // We don't consider something to have a dynamic type if + // it is the same as the static type. So compare against + // the value we were handed: + + clang::ASTContext *in_ast_ctx = in_value.GetClangAST (); + clang::ASTContext *this_ast_ctx = this_type->GetClangAST (); + if (in_ast_ctx != this_ast_ctx + || !ClangASTContext::AreTypesSame (in_ast_ctx, + in_value.GetClangType(), + this_type->GetClangFullType())) + { + class_type_or_name.SetTypeSP (this_type); + return true; + } + return false; + } + } + } } - - if (!dynamic_type_sp) + else return false; // The offset_to_top is two pointers above the address. diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h index 421ac12725f6a8971c5e2907f43d79483d2984cd..966090d4aea826f0e69b0f468dc429cba07c3477 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h @@ -31,7 +31,7 @@ namespace lldb_private { IsVTableName (const char *name); virtual bool - GetDynamicValue (ValueObject &in_value, lldb::TypeSP &type_sp, Address &address); + GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &address); virtual bool CouldHaveDynamicValue (ValueObject &in_value); diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp index 08af0b6843193cf579bcf5a1500ff775bf1bdc23..62eac38e6e219738226f7d6a4b82f00121a5a6d5 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp @@ -198,7 +198,7 @@ AppleObjCRuntime::CouldHaveDynamicValue (ValueObject &in_value) } bool -AppleObjCRuntime::GetDynamicValue (ValueObject &in_value, lldb::TypeSP &type_sp, Address &address) +AppleObjCRuntime::GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &address) { return false; } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h index e7e0f71d95687cfc62a32ef759bd790620176a97..f3eb074cedbb9399d9a0007174ce16259e175bba 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h @@ -41,7 +41,7 @@ public: CouldHaveDynamicValue (ValueObject &in_value); virtual bool - GetDynamicValue (ValueObject &in_value, lldb::TypeSP &type_sp, Address &address); + GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &address); // These are the ObjC specific functions. diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp index 000f14e3df19cf631c7bdab4c0d8ac82273922eb..fe29b628aaeff3d7e629beb84614c680eeb05a97 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp @@ -41,7 +41,7 @@ static const char *pluginDesc = "Apple Objective C Language Runtime - Version 1" static const char *pluginShort = "language.apple.objc.v1"; bool -AppleObjCRuntimeV1::GetDynamicValue (ValueObject &in_value, lldb::TypeSP &type_sp, Address &address) +AppleObjCRuntimeV1::GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &address) { return false; } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h index 975c018b4e9ae64bd1a6d830f98d6a486ba0f071..7a43ca33c85185544c2c703e7cd7dba2f8e98ab9 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.h @@ -32,7 +32,7 @@ public: // These are generic runtime functions: virtual bool - GetDynamicValue (ValueObject &in_value, lldb::TypeSP &type_sp, Address &address); + GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &address); virtual ClangUtilityFunction * CreateObjectChecker (const char *); diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index c6e3533a09dc21df18d4e48bbc9d2fe6095ade0d..42e7aee8c88a92d4e6fa69d0c78f825b724c45d5 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -40,15 +40,359 @@ static const char *pluginName = "AppleObjCRuntimeV2"; static const char *pluginDesc = "Apple Objective C Language Runtime - Version 2"; static const char *pluginShort = "language.apple.objc.v2"; + +const char *AppleObjCRuntimeV2::g_find_class_name_function_name = "__lldb_apple_objc_v2_find_class_name"; +const char *AppleObjCRuntimeV2::g_find_class_name_function_body = " \n\ +extern \"C\" \n\ +{ \n\ + extern void *gdb_class_getClass (void *objc_class); \n\ + extern void *class_getName(void *objc_class); \n\ + extern int printf(const char *format, ...); \n\ +} \n\ + \n\ +struct __lldb_objc_object { \n\ + void *isa; \n\ +}; \n\ + \n\ +extern \"C\" void *__lldb_apple_objc_v2_find_class_name ( \n\ + __lldb_objc_object *object_ptr, \n\ + int debug) \n\ +{ \n\ + void *name = 0; \n\ + if (debug) \n\ + printf (\"\\n*** Called in v2_find_class_name with object: 0x%p\\n\", object_ptr); \n\ + // Call gdb_class_getClass so we can tell if the class is good. \n\ + void *objc_class = gdb_class_getClass (object_ptr->isa); \n\ + if (objc_class) \n\ + { \n\ + void *actual_class = (void *) [(id) object_ptr class]; \n\ + if (actual_class != 0) \n\ + name = class_getName((void *) actual_class); \n\ + if (debug) \n\ + printf (\"\\n*** Found name: %s\\n\", name ? name : \"\"); \n\ + } \n\ + else if (debug) \n\ + printf (\"\\n*** gdb_class_getClass returned NULL\\n\"); \n\ + return name; \n\ +} \n\ +"; + +const char *AppleObjCRuntimeV2::g_objc_class_symbol_prefix = "OBJC_CLASS_$_"; +const char *AppleObjCRuntimeV2::g_objc_class_data_section_name = "__objc_data"; + AppleObjCRuntimeV2::AppleObjCRuntimeV2 (Process *process, ModuleSP &objc_module_sp) : - lldb_private::AppleObjCRuntime (process) + lldb_private::AppleObjCRuntime (process), + m_get_class_name_args(LLDB_INVALID_ADDRESS), + m_get_class_name_args_mutex(Mutex::eMutexTypeNormal) { m_has_object_getClass = (objc_module_sp->FindFirstSymbolWithNameAndType(ConstString("gdb_object_getClass")) != NULL); } bool -AppleObjCRuntimeV2::GetDynamicValue (ValueObject &in_value, lldb::TypeSP &type_sp, Address &address) +AppleObjCRuntimeV2::RunFunctionToFindClassName(lldb::addr_t object_addr, Thread *thread, char *name_dst, size_t max_name_len) { + // Since we are going to run code we have to make sure only one thread at a time gets to try this. + Mutex::Locker (m_get_class_name_args_mutex); + + StreamString errors; + + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); // FIXME - a more appropriate log channel? + + int32_t debug; + if (log) + debug = 1; + else + debug = 0; + + ValueList dispatch_values; + + Value void_ptr_value; + ClangASTContext *clang_ast_context = m_process->GetTarget().GetScratchClangASTContext(); + + lldb::clang_type_t clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false); + void_ptr_value.SetValueType (Value::eValueTypeScalar); + void_ptr_value.SetContext (Value::eContextTypeClangType, clang_void_ptr_type); + void_ptr_value.GetScalar() = object_addr; + + dispatch_values.PushValue (void_ptr_value); + + Value int_value; + lldb::clang_type_t clang_int_type = clang_ast_context->GetBuiltinTypeForEncodingAndBitSize(lldb::eEncodingSint, 32); + int_value.SetValueType (Value::eValueTypeScalar); + int_value.SetContext (Value::eContextTypeClangType, clang_int_type); + int_value.GetScalar() = debug; + + dispatch_values.PushValue (int_value); + + ExecutionContext exe_ctx; + thread->CalculateExecutionContext(exe_ctx); + + Address find_class_name_address; + + if (!m_get_class_name_code.get()) + { + m_get_class_name_code.reset (new ClangUtilityFunction (g_find_class_name_function_body, + g_find_class_name_function_name)); + + if (!m_get_class_name_code->Install(errors, exe_ctx)) + { + if (log) + log->Printf ("Failed to install implementation lookup: %s.", errors.GetData()); + m_get_class_name_code.reset(); + return false; + } + find_class_name_address.Clear(); + find_class_name_address.SetOffset(m_get_class_name_code->StartAddress()); + } + else + { + find_class_name_address.Clear(); + find_class_name_address.SetOffset(m_get_class_name_code->StartAddress()); + } + + // Next make the runner function for our implementation utility function. + if (!m_get_class_name_function.get()) + { + m_get_class_name_function.reset(new ClangFunction (*m_process, + clang_ast_context, + clang_void_ptr_type, + find_class_name_address, + dispatch_values)); + + errors.Clear(); + unsigned num_errors = m_get_class_name_function->CompileFunction(errors); + if (num_errors) + { + if (log) + log->Printf ("Error compiling function: \"%s\".", errors.GetData()); + return false; + } + + errors.Clear(); + if (!m_get_class_name_function->WriteFunctionWrapper(exe_ctx, errors)) + { + if (log) + log->Printf ("Error Inserting function: \"%s\".", errors.GetData()); + return false; + } + } + + if (m_get_class_name_code.get() == NULL || m_get_class_name_function.get() == NULL) + return false; + + // Finally, write down the arguments, and call the function. Note that we will re-use the same space in the target + // for the args. We're locking this to ensure that only one thread at a time gets to call this function, so we don't + // have to worry about overwriting the arguments. + + if (!m_get_class_name_function->WriteFunctionArguments (exe_ctx, m_get_class_name_args, find_class_name_address, dispatch_values, errors)) + return false; + + bool stop_others = true; + bool try_all_threads = true; + bool unwind_on_error = true; + + ExecutionResults results = m_get_class_name_function->ExecuteFunction (exe_ctx, + &m_get_class_name_args, + errors, + stop_others, + 1000000, + try_all_threads, + unwind_on_error, + void_ptr_value); + + if (results != eExecutionCompleted) + { + if (log) + log->Printf("Error evaluating our find class name function: %d.\n", results); + return false; + } + + lldb::addr_t result_ptr = void_ptr_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + size_t chars_read = m_process->ReadCStringFromMemory (result_ptr, name_dst, max_name_len); + + // If we exhausted our buffer before finding a NULL we're probably off in the weeds somewhere... + if (chars_read == max_name_len) + return false; + else + return true; + +} + +bool +AppleObjCRuntimeV2::GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &address) +{ + // The Runtime is attached to a particular process, you shouldn't pass in a value from another process. + assert (in_value.GetUpdatePoint().GetProcess() == m_process); + + // Make sure we can have a dynamic value before starting... + if (CouldHaveDynamicValue (in_value)) + { + // First job, pull out the address at 0 offset from the object That will be the ISA pointer. + AddressType address_type; + lldb::addr_t original_ptr = in_value.GetPointerValue(address_type, true); + + // ObjC only has single inheritance, so the objects all start at the same pointer value. + address.SetSection (NULL); + address.SetOffset (original_ptr); + + if (original_ptr == LLDB_INVALID_ADDRESS) + return false; + + Target *target = m_process->CalculateTarget(); + + char memory_buffer[16]; + DataExtractor data(memory_buffer, sizeof(memory_buffer), + m_process->GetByteOrder(), + m_process->GetAddressByteSize()); + size_t address_byte_size = m_process->GetAddressByteSize(); + Error error; + size_t bytes_read = m_process->ReadMemory (original_ptr, + memory_buffer, + address_byte_size, + error); + if (!error.Success() || (bytes_read != address_byte_size)) + { + return false; + } + + uint32_t offset_ptr = 0; + lldb::addr_t isa_addr = data.GetAddress (&offset_ptr); + + if (offset_ptr == 0) + return false; + + // Make sure the class address is readable, otherwise this is not a good object: + bytes_read = m_process->ReadMemory (isa_addr, + memory_buffer, + address_byte_size, + error); + if (bytes_read != address_byte_size) + return false; + + // First check the cache... + + SymbolContext sc; + + class_type_or_name = LookupInClassNameCache (isa_addr); + + if (!class_type_or_name.IsEmpty()) + { + if (class_type_or_name.GetTypeSP() != NULL) + return true; + else + return false; + } + + const char *class_name = NULL; + Address isa_address; + target->GetSectionLoadList().ResolveLoadAddress (isa_addr, isa_address); + + if (isa_address.IsValid()) + { + // If the ISA pointer points to one of the sections in the binary, then see if we can + // get the class name from the symbols. + + const Section *section = isa_address.GetSection(); + + if (section) + { + // If this points to a section that we know about, then this is + // some static class or nothing. See if it is in the right section + // and if its name is the right form. + ConstString section_name = section->GetName(); + if (section_name == ConstString(g_objc_class_data_section_name)) + { + isa_address.CalculateSymbolContext(&sc); + if (sc.symbol) + { + class_name = sc.symbol->GetName().AsCString(); + if (strstr (class_name, g_objc_class_symbol_prefix) == class_name) + class_name += strlen (g_objc_class_symbol_prefix); + else + return false; + } + } + } + } + + char class_buffer[1024]; + if (class_name == NULL) + { + // If the class address didn't point into the binary, or + // it points into the right section but there wasn't a symbol + // there, try to look it up by calling the class method in the target. + ExecutionContextScope *exe_scope = in_value.GetUpdatePoint().GetExecutionContextScope(); + Thread *thread_to_use; + if (exe_scope) + thread_to_use = exe_scope->CalculateThread(); + + if (thread_to_use == NULL) + thread_to_use = m_process->GetThreadList().GetSelectedThread().get(); + + if (thread_to_use == NULL) + return false; + + if (!RunFunctionToFindClassName (original_ptr, thread_to_use, class_buffer, 1024)) + return false; + + class_name = class_buffer; + + } + + if (class_name != NULL && *class_name != '\0') + { + class_type_or_name.SetName (class_name); + + TypeList class_types; + uint32_t num_matches = target->GetImages().FindTypes (sc, + class_type_or_name.GetName(), + true, + UINT32_MAX, + class_types); + if (num_matches == 1) + { + class_type_or_name.SetTypeSP (class_types.GetTypeAtIndex(0)); + return true; + } + else + { + for (size_t i = 0; i < num_matches; i++) + { + lldb::TypeSP this_type(class_types.GetTypeAtIndex(i)); + if (this_type) + { + if (ClangASTContext::IsObjCClassType(this_type->GetClangFullType())) + { + // There can only be one type with a given name, + // so we've just found duplicate definitions, and this + // one will do as well as any other. + // We don't consider something to have a dynamic type if + // it is the same as the static type. So compare against + // the value we were handed: + + clang::ASTContext *in_ast_ctx = in_value.GetClangAST (); + clang::ASTContext *this_ast_ctx = this_type->GetClangAST (); + if (in_ast_ctx != this_ast_ctx + || !ClangASTContext::AreTypesSame (in_ast_ctx, + in_value.GetClangType(), + this_type->GetClangFullType())) + { + class_type_or_name.SetTypeSP (this_type); + } + break; + } + } + } + } + + AddToClassNameCache (isa_addr, class_type_or_name); + if (class_type_or_name.GetTypeSP()) + return true; + else + return false; + } + } + return false; } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h index 0015dd2f424c50f087c70570aa390b7e213a1158..93395ff2ab56de7aadd104e680799e4183c9a9fd 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h @@ -23,7 +23,7 @@ #include "AppleThreadPlanStepThroughObjCTrampoline.h" namespace lldb_private { - + class AppleObjCRuntimeV2 : public AppleObjCRuntime { @@ -32,7 +32,7 @@ public: // These are generic runtime functions: virtual bool - GetDynamicValue (ValueObject &in_value, lldb::TypeSP &type_sp, Address &address); + GetDynamicTypeAndAddress (ValueObject &in_value, TypeAndOrName &class_type_or_name, Address &address); virtual ClangUtilityFunction * CreateObjectChecker (const char *); @@ -76,7 +76,18 @@ protected: private: AppleObjCRuntimeV2(Process *process, ModuleSP &objc_module_sp); - bool m_has_object_getClass; + bool RunFunctionToFindClassName (lldb::addr_t class_addr, Thread *thread, char *name_dst, size_t max_name_len); + + bool m_has_object_getClass; + std::auto_ptr m_get_class_name_function; + std::auto_ptr m_get_class_name_code; + lldb::addr_t m_get_class_name_args; + Mutex m_get_class_name_args_mutex; + + static const char *g_find_class_name_function_name; + static const char *g_find_class_name_function_body; + static const char *g_objc_class_symbol_prefix; + static const char *g_objc_class_data_section_name; }; } // namespace lldb_private diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp index 31fad29847d2bdb82d9017a69d1f8b8785ae5b81..71cbcdd2953b84603980973301df65e7934ac931 100644 --- a/lldb/source/Symbol/Type.cpp +++ b/lldb/source/Symbol/Type.cpp @@ -662,3 +662,75 @@ Type::CreateClangRValueReferenceType (Type *type) } +TypeAndOrName::TypeAndOrName () : m_type_sp(), m_type_name() +{ + +} + +TypeAndOrName::TypeAndOrName (TypeSP &in_type_sp) : m_type_sp(in_type_sp) +{ + if (in_type_sp) + m_type_name = in_type_sp->GetName(); +} + +TypeAndOrName::TypeAndOrName (const char *in_type_str) : m_type_name(in_type_str) +{ +} + +TypeAndOrName::TypeAndOrName (const TypeAndOrName &rhs) : m_type_sp (rhs.m_type_sp), m_type_name (rhs.m_type_name) +{ + +} + +TypeAndOrName::TypeAndOrName (ConstString &in_type_const_string) : m_type_name (in_type_const_string) +{ +} + +TypeAndOrName & +TypeAndOrName::operator= (const TypeAndOrName &rhs) +{ + if (this != &rhs) + { + m_type_name = rhs.m_type_name; + m_type_sp = rhs.m_type_sp; + } + return *this; +} + +ConstString +TypeAndOrName::GetName () const +{ + if (m_type_sp) + return m_type_sp->GetName(); + else + return m_type_name; +} + +void +TypeAndOrName::SetName (ConstString &type_name_const_str) +{ + m_type_name = type_name_const_str; +} + +void +TypeAndOrName::SetName (const char *type_name_str) +{ + m_type_name.SetCString (type_name_str); +} + +void +TypeAndOrName::SetTypeSP (lldb::TypeSP type_sp) +{ + m_type_sp = type_sp; + if (type_sp) + m_type_name = type_sp->GetName(); +} + +bool +TypeAndOrName::IsEmpty() +{ + if (m_type_name || m_type_sp) + return false; + else + return true; +} diff --git a/lldb/source/Target/ObjCLanguageRuntime.cpp b/lldb/source/Target/ObjCLanguageRuntime.cpp index bc6e8a0629bc3dbbf1d1df1ea4798ef6c7a75149..1d5755691836189e38bb1e1a214e211fedc75131 100644 --- a/lldb/source/Target/ObjCLanguageRuntime.cpp +++ b/lldb/source/Target/ObjCLanguageRuntime.cpp @@ -12,6 +12,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Core/ValueObject.h" #include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" #include "lldb/Target/ObjCLanguageRuntime.h" using namespace lldb; @@ -50,3 +51,45 @@ ObjCLanguageRuntime::LookupInMethodCache (lldb::addr_t class_addr, lldb::addr_t return (*pos).second; return LLDB_INVALID_ADDRESS; } + +void +ObjCLanguageRuntime::AddToClassNameCache (lldb::addr_t class_addr, const char *name, lldb::TypeSP type_sp) +{ + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + { + log->Printf ("Caching: class 0x%llx name: %s.", class_addr, name); + } + + TypeAndOrName class_type_or_name; + + if (type_sp != NULL) + class_type_or_name.SetTypeSP (type_sp); + else if (name && *name != '\0') + class_type_or_name.SetName (name); + else + return; + m_class_name_cache.insert (std::pair (class_addr, class_type_or_name)); +} + +void +ObjCLanguageRuntime::AddToClassNameCache (lldb::addr_t class_addr, const TypeAndOrName &class_type_or_name) +{ + LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + { + log->Printf ("Caching: class 0x%llx name: %s.", class_addr, class_type_or_name.GetName().AsCString()); + } + + m_class_name_cache.insert (std::pair (class_addr, class_type_or_name)); +} + +TypeAndOrName +ObjCLanguageRuntime::LookupInClassNameCache (lldb::addr_t class_addr) +{ + ClassNameMap::iterator pos, end = m_class_name_cache.end(); + pos = m_class_name_cache.find (class_addr); + if (pos != end) + return (*pos).second; + return TypeAndOrName (); +} diff --git a/lldb/test/objc-dynamic-value/Makefile b/lldb/test/objc-dynamic-value/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d7c1e6d1bd8bf36127c67cb8bca0001647156e0b --- /dev/null +++ b/lldb/test/objc-dynamic-value/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../make + +OBJC_SOURCES := dynamic-value.m +LDFLAGS = $(CFLAGS) -lobjc -framework Foundation + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/objc-dynamic-value/TestObjCDynamicValue.py b/lldb/test/objc-dynamic-value/TestObjCDynamicValue.py new file mode 100644 index 0000000000000000000000000000000000000000..f0b2ce34034bcc418e850f08b106d2459c95b80e --- /dev/null +++ b/lldb/test/objc-dynamic-value/TestObjCDynamicValue.py @@ -0,0 +1,165 @@ +""" +Use lldb Python API to test dynamic values in ObjC +""" + +import os, time +import re +import unittest2 +import lldb, lldbutil +from lldbtest import * + +class ObjCDynamicValueTestCase(TestBase): + + mydir = "objc-dynamic-value" + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @python_api_test + def test_get_dynamic_objc_vals_with_dsym(self): + """Test fetching ObjC dynamic values.""" + self.buildDsym() + self.do_get_dynamic_vals() + + @python_api_test + def test_get_objc_dynamic_vals_with_dwarf(self): + """Test fetching ObjC dynamic values.""" + self.buildDwarf() + self.do_get_dynamic_vals() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + # Find the line number to break for main.c. + + self.source_name = 'dynamic-value.m' + self.set_property_line = line_number(self.source_name, '// This is the line in setProperty, make sure we step to here.') + self.handle_SourceBase = line_number(self.source_name, + '// Break here to check dynamic values.') + self.main_before_setProperty_line = line_number(self.source_name, + '// Break here to see if we can step into real method.') + + def examine_SourceDerived_ptr (self, object): + self.assertTrue (object.IsValid()) + self.assertTrue (object.GetTypeName().find ('SourceDerived') != -1) + derivedValue = object.GetChildMemberWithName ('_derivedValue') + self.assertTrue (derivedValue.IsValid()) + self.assertTrue (int (derivedValue.GetValue(), 0) == 30) + + def do_get_dynamic_vals(self): + """Make sure we get dynamic values correctly both for compiled in classes and dynamic ones""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target from the debugger. + + target = self.dbg.CreateTarget (exe) + self.assertTrue(target.IsValid(), VALID_TARGET) + + # Set up our breakpoints: + + handle_SourceBase_bkpt = target.BreakpointCreateByLocation(self.source_name, self.handle_SourceBase) + self.assertTrue(handle_SourceBase_bkpt.IsValid() and + handle_SourceBase_bkpt.GetNumLocations() == 1, + VALID_BREAKPOINT) + + main_before_setProperty_bkpt = target.BreakpointCreateByLocation(self.source_name, self.main_before_setProperty_line) + self.assertTrue(main_before_setProperty_bkpt.IsValid() and + main_before_setProperty_bkpt.GetNumLocations() == 1, + VALID_BREAKPOINT) + + # Now launch the process, and do not stop at the entry point. + self.process = target.LaunchSimple (None, None, os.getcwd()) + + self.assertTrue(self.process.GetState() == lldb.eStateStopped, + PROCESS_STOPPED) + + threads = lldbutil.get_threads_stopped_at_breakpoint (self.process, main_before_setProperty_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + # + # At this point, myObserver has a Source pointer that is actually a KVO swizzled SourceDerived + # make sure we can get that properly: + + frame = thread.GetFrameAtIndex(0) + myObserver = frame.FindVariable('myObserver') + self.assertTrue (myObserver.IsValid()) + myObserver_source = myObserver.GetChildMemberWithName ('_source') + self.examine_SourceDerived_ptr (myObserver_source) + + # The "frame var" code uses another path to get into children, so let's + # make sure that works as well: + + result = lldb.SBCommandReturnObject() + + self.expect('frame var -d 1 myObserver->_source', 'frame var finds its way into a child member', + patterns = ['\(SourceDerived \*\)']) + + # This test is not entirely related to the main thrust of this test case, but since we're here, + # try stepping into setProperty, and make sure we get into the version in Source: + + thread.StepInto() + + threads = lldbutil.get_stopped_threads (self.process, lldb.eStopReasonPlanComplete) + self.assertTrue (len(threads) == 1) + line_entry = threads[0].GetFrameAtIndex(0).GetLineEntry() + self.assertTrue (line_entry.GetLine() == self.set_property_line) + self.assertTrue (line_entry.GetFileSpec().GetFilename() == self.source_name) + + # Okay, back to the main business. Continue to the handle_SourceBase and make sure we get the correct dynamic value. + + threads = lldbutil.continue_to_breakpoint (self.process, handle_SourceBase_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + frame = thread.GetFrameAtIndex(0) + + # Get "object" using FindVariable: + + noDynamic = False + useDynamic = True + + object_static = frame.FindVariable ('object', noDynamic) + object_dynamic = frame.FindVariable ('object', useDynamic) + + # Delete this object to make sure that this doesn't cause havoc with the dynamic object that depends on it. + del (object_static) + + self.examine_SourceDerived_ptr (object_dynamic) + + # Get "this" using FindValue, make sure that works too: + object_static = frame.FindValue ('object', lldb.eValueTypeVariableArgument, noDynamic) + object_dynamic = frame.FindValue ('object', lldb.eValueTypeVariableArgument, useDynamic) + del (object_static) + self.examine_SourceDerived_ptr (object_dynamic) + + # Get "this" using the EvaluateExpression: + # These tests fail for now because EvaluateExpression doesn't currently support dynamic typing... + #object_static = frame.EvaluateExpression ('object', False) + #object_dynamic = frame.EvaluateExpression ('object', True) + #self.examine_value_object_of_object_ptr (object_static, object_dynamic, myB_loc) + + # Continue again to the handle_SourceBase and make sure we get the correct dynamic value. + # This one looks exactly the same, but in fact this is an "un-KVO'ed" version of SourceBase, so + # its isa pointer points to SourceBase not NSKVOSourceBase or whatever... + + threads = lldbutil.continue_to_breakpoint (self.process, handle_SourceBase_bkpt) + self.assertTrue (len(threads) == 1) + thread = threads[0] + + frame = thread.GetFrameAtIndex(0) + + # Get "object" using FindVariable: + + object_static = frame.FindVariable ('object', noDynamic) + object_dynamic = frame.FindVariable ('object', useDynamic) + + # Delete this object to make sure that this doesn't cause havoc with the dynamic object that depends on it. + del (object_static) + + self.examine_SourceDerived_ptr (object_dynamic) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/objc-dynamic-value/dynamic-value.m b/lldb/test/objc-dynamic-value/dynamic-value.m new file mode 100644 index 0000000000000000000000000000000000000000..60a506efd8e82abbc7ca5048b7af395b806e15e7 --- /dev/null +++ b/lldb/test/objc-dynamic-value/dynamic-value.m @@ -0,0 +1,147 @@ +#import + +// SourceBase will be the base class of Source. We'll pass a Source object into a +// function as a SourceBase, and then see if the dynamic typing can get us through the KVO +// goo and all the way back to Source. + +@interface SourceBase: NSObject +{ + uint32_t _value; +} +- (SourceBase *) init; +- (uint32_t) getValue; +@end + +@implementation SourceBase +- (SourceBase *) init +{ + [super init]; + _value = 10; + return self; +} +- (uint32_t) getValue +{ + return _value; +} +@end + +// Source is a class that will be observed by the Observer class below. +// When Observer sets itself up to observe this property (in initWithASource) +// the KVO system will overwrite the "isa" pointer of the object with the "kvo'ed" +// one. + +@interface Source : SourceBase +{ + int _property; +} +- (Source *) init; +- (void) setProperty: (int) newValue; +@end + +@implementation Source +- (Source *) init +{ + [super init]; + _property = 20; + return self; +} +- (void) setProperty: (int) newValue +{ + _property = newValue; // This is the line in setProperty, make sure we step to here. +} +@end + +@interface SourceDerived : Source +{ + int _derivedValue; +} +- (SourceDerived *) init; +- (uint32_t) getValue; +@end + +@implementation SourceDerived +- (SourceDerived *) init +{ + [super init]; + _derivedValue = 30; + return self; +} +- (uint32_t) getValue +{ + return _derivedValue; +} +@end + +// Observer is the object that will watch Source and cause KVO to swizzle it... + +@interface Observer : NSObject +{ + Source *_source; +} ++ (Observer *) observerWithSource: (Source *) source; +- (Observer *) initWithASource: (Source *) source; +- (void) observeValueForKeyPath: (NSString *) path + ofObject: (id) object + change: (NSDictionary *) change + context: (void *) context; +@end + +@implementation Observer + ++ (Observer *) observerWithSource: (Source *) inSource; +{ + Observer *retval; + + retval = [[Observer alloc] initWithASource: inSource]; + return retval; +} + +- (Observer *) initWithASource: (Source *) source +{ + [super init]; + _source = source; + [_source addObserver: self + forKeyPath: @"property" + options: (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) + context: NULL]; + return self; +} + +- (void) observeValueForKeyPath: (NSString *) path + ofObject: (id) object + change: (NSDictionary *) change + context: (void *) context +{ + printf ("Observer function called.\n"); + return; +} +@end + +uint32_t +handle_SourceBase (SourceBase *object) +{ + return [object getValue]; // Break here to check dynamic values. +} + +int main () +{ + SourceDerived *mySource; + Observer *myObserver; + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + mySource = [[SourceDerived alloc] init]; + myObserver = [Observer observerWithSource: mySource]; + + [mySource setProperty: 5]; // Break here to see if we can step into real method. + + uint32_t return_value = handle_SourceBase (mySource); + + SourceDerived *unwatchedSource = [[SourceDerived alloc] init]; + + return_value = handle_SourceBase (unwatchedSource); + + [pool release]; + return 0; + +}