diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h @@ -293,19 +293,30 @@ } }; + /// Abstraction to read the Objective-C class info. + class ClassInfoExtractor { + public: + ClassInfoExtractor(AppleObjCRuntimeV2 &runtime) : m_runtime(runtime) {} + std::mutex &GetMutex() { return m_mutex; } + + protected: + /// The lifetime of this object is tied to that of the runtime. + AppleObjCRuntimeV2 &m_runtime; + std::mutex m_mutex; + }; + /// We can read the class info from the Objective-C runtime using /// gdb_objc_realized_classes or objc_copyRealizedClassList. The latter is /// preferred because it includes lazily named classes, but it's not always /// available or safe to call. /// - /// We potentially need both for the same process, - /// because we may need to use gdb_objc_realized_classes until dyld is - /// initialized and then switch over to objc_copyRealizedClassList for lazily - /// named classes. - class DynamicClassInfoExtractor { + /// We potentially need both for the same process, because we may need to use + /// gdb_objc_realized_classes until dyld is initialized and then switch over + /// to objc_copyRealizedClassList for lazily named classes. + class DynamicClassInfoExtractor : public ClassInfoExtractor { public: DynamicClassInfoExtractor(AppleObjCRuntimeV2 &runtime) - : m_runtime(runtime) {} + : ClassInfoExtractor(runtime) {} enum Helper { gdb_objc_realized_classes, objc_copyRealizedClassList }; @@ -317,24 +328,45 @@ UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx, Helper helper); lldb::addr_t &GetClassInfoArgs(Helper helper); - std::mutex &GetMutex() { return m_mutex; } private: std::unique_ptr GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx, std::string code, std::string name); - /// The lifetime of this object is tied to that of the runtime. - AppleObjCRuntimeV2 &m_runtime; - std::mutex m_mutex; + /// Helper to read class info using the gdb_objc_realized_classes. + struct gdb_objc_realized_classes_helper { + std::unique_ptr utility_function; + lldb::addr_t args = LLDB_INVALID_ADDRESS; + }; + + /// Helper to read class info using objc_copyRealizedClassList. + struct objc_copyRealizedClassList_helper { + std::unique_ptr utility_function; + lldb::addr_t args = LLDB_INVALID_ADDRESS; + }; + + gdb_objc_realized_classes_helper m_gdb_objc_realized_classes_helper; + objc_copyRealizedClassList_helper m_objc_copyRealizedClassList_helper; + }; + + /// Abstraction to read the Objective-C class info from the shared cache. + class SharedCacheClassInfoExtractor : public ClassInfoExtractor { + public: + SharedCacheClassInfoExtractor(AppleObjCRuntimeV2 &runtime) + : ClassInfoExtractor(runtime) {} - /// Utility function to read class info using gdb_objc_realized_classes. - std::unique_ptr m_get_class_info_code; - lldb::addr_t m_get_class_info_args = LLDB_INVALID_ADDRESS; + UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx); + lldb::addr_t &GetClassInfoArgs() { return m_args; } + std::mutex &GetMutex() { return m_mutex; } + + private: + std::unique_ptr + GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx); - /// Utility function to read class info using objc_copyRealizedClassList. - std::unique_ptr m_get_class_info2_code; - lldb::addr_t m_get_class_info2_args = LLDB_INVALID_ADDRESS; + std::unique_ptr m_utility_function; + lldb::addr_t m_args = LLDB_INVALID_ADDRESS; + std::mutex m_mutex; }; AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp); @@ -383,11 +415,8 @@ lldb::ModuleSP m_objc_module_sp; - DynamicClassInfoExtractor m_class_info_extractor; - - std::unique_ptr m_get_shared_cache_class_info_code; - lldb::addr_t m_get_shared_cache_class_info_args; - std::mutex m_get_shared_cache_class_info_args_mutex; + DynamicClassInfoExtractor m_dynamic_class_info_extractor; + SharedCacheClassInfoExtractor m_shared_cache_class_info_extractor; std::unique_ptr m_decl_vendor_up; lldb::addr_t m_tagged_pointer_obfuscator; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -482,9 +482,8 @@ AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process, const ModuleSP &objc_module_sp) : AppleObjCRuntime(process), m_objc_module_sp(objc_module_sp), - m_class_info_extractor(*this), m_get_shared_cache_class_info_code(), - m_get_shared_cache_class_info_args(LLDB_INVALID_ADDRESS), - m_get_shared_cache_class_info_args_mutex(), m_decl_vendor_up(), + m_dynamic_class_info_extractor(*this), + m_shared_cache_class_info_extractor(*this), m_decl_vendor_up(), m_tagged_pointer_obfuscator(LLDB_INVALID_ADDRESS), m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS), m_hash_signature(), m_has_object_getClass(false), m_has_objc_copyRealizedClassList(false), @@ -1419,18 +1418,20 @@ ExecutionContext &exe_ctx, Helper helper) { switch (helper) { case gdb_objc_realized_classes: { - if (!m_get_class_info_code) - m_get_class_info_code = GetClassInfoUtilityFunctionImpl( - exe_ctx, g_get_dynamic_class_info_body, - g_get_dynamic_class_info_name); - return m_get_class_info_code.get(); + if (!m_gdb_objc_realized_classes_helper.utility_function) + m_gdb_objc_realized_classes_helper.utility_function = + GetClassInfoUtilityFunctionImpl(exe_ctx, + g_get_dynamic_class_info_body, + g_get_dynamic_class_info_name); + return m_gdb_objc_realized_classes_helper.utility_function.get(); } case objc_copyRealizedClassList: { - if (!m_get_class_info2_code) - m_get_class_info2_code = GetClassInfoUtilityFunctionImpl( - exe_ctx, g_get_dynamic_class_info2_body, - g_get_dynamic_class_info2_name); - return m_get_class_info2_code.get(); + if (!m_objc_copyRealizedClassList_helper.utility_function) + m_objc_copyRealizedClassList_helper.utility_function = + GetClassInfoUtilityFunctionImpl(exe_ctx, + g_get_dynamic_class_info2_body, + g_get_dynamic_class_info2_name); + return m_objc_copyRealizedClassList_helper.utility_function.get(); } }; } @@ -1439,9 +1440,9 @@ AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoArgs(Helper helper) { switch (helper) { case gdb_objc_realized_classes: - return m_get_class_info_args; + return m_gdb_objc_realized_classes_helper.args; case objc_copyRealizedClassList: - return m_get_class_info2_args; + return m_objc_copyRealizedClassList_helper.args; } } @@ -1460,6 +1461,95 @@ return DynamicClassInfoExtractor::gdb_objc_realized_classes; } +std::unique_ptr +AppleObjCRuntimeV2::SharedCacheClassInfoExtractor:: + GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES)); + + LLDB_LOG(log, "Creating utility function {0}", + g_get_shared_cache_class_info_name); + + TypeSystemClang *ast = + ScratchTypeSystemClang::GetForTarget(exe_ctx.GetTargetRef()); + if (!ast) + return {}; + + // If the inferior objc.dylib has the class_getNameRaw function, use that in + // our jitted expression. Else fall back to the old class_getName. + static ConstString g_class_getName_symbol_name("class_getName"); + static ConstString g_class_getNameRaw_symbol_name( + "objc_debug_class_getNameRaw"); + + ConstString class_name_getter_function_name = + m_runtime.HasSymbol(g_class_getNameRaw_symbol_name) + ? g_class_getNameRaw_symbol_name + : g_class_getName_symbol_name; + + // Substitute in the correct class_getName / class_getNameRaw function name, + // concatenate the two parts of our expression text. The format string has + // two %s's, so provide the name twice. + std::string shared_class_expression; + llvm::raw_string_ostream(shared_class_expression) + << llvm::format(g_shared_cache_class_name_funcptr, + class_name_getter_function_name.AsCString(), + class_name_getter_function_name.AsCString()); + + shared_class_expression += g_get_shared_cache_class_info_body; + + auto utility_fn_or_error = exe_ctx.GetTargetRef().CreateUtilityFunction( + std::move(shared_class_expression), g_get_shared_cache_class_info_name, + eLanguageTypeC, exe_ctx); + + if (!utility_fn_or_error) { + LLDB_LOG_ERROR( + log, utility_fn_or_error.takeError(), + "Failed to get utility function for implementation lookup: {0}"); + return nullptr; + } + + // Make some types for our arguments. + CompilerType clang_uint32_t_type = + ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); + CompilerType clang_void_pointer_type = + ast->GetBasicType(eBasicTypeVoid).GetPointerType(); + + // Next make the function caller for our implementation utility function. + ValueList arguments; + Value value; + value.SetValueType(Value::ValueType::Scalar); + value.SetCompilerType(clang_void_pointer_type); + arguments.PushValue(value); + arguments.PushValue(value); + + value.SetValueType(Value::ValueType::Scalar); + value.SetCompilerType(clang_uint32_t_type); + arguments.PushValue(value); + arguments.PushValue(value); + + std::unique_ptr utility_fn = std::move(*utility_fn_or_error); + + Status error; + utility_fn->MakeFunctionCaller(clang_uint32_t_type, arguments, + exe_ctx.GetThreadSP(), error); + + if (error.Fail()) { + LLDB_LOG(log, + "Failed to make function caller for implementation lookup: {0}.", + error.AsCString()); + return {}; + } + + return utility_fn; +} + +UtilityFunction * +AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::GetClassInfoUtilityFunction( + ExecutionContext &exe_ctx) { + if (!m_utility_function) + m_utility_function = GetClassInfoUtilityFunctionImpl(exe_ctx); + return m_utility_function.get(); +} + AppleObjCRuntimeV2::DescriptorMapUpdateResult AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic( RemoteNXMapTable &hash_table) { @@ -1494,7 +1584,7 @@ // Compute which helper we're going to use for this update. const DynamicClassInfoExtractor::Helper helper = - m_class_info_extractor.ComputeHelper(); + m_dynamic_class_info_extractor.ComputeHelper(); // Read the total number of classes from the hash table const uint32_t num_classes = @@ -1507,7 +1597,8 @@ } UtilityFunction *get_class_info_code = - m_class_info_extractor.GetClassInfoUtilityFunction(exe_ctx, helper); + m_dynamic_class_info_extractor.GetClassInfoUtilityFunction(exe_ctx, + helper); if (!get_class_info_code) { // The callee will have already logged a useful error message. return DescriptorMapUpdateResult::Fail(); @@ -1538,7 +1629,7 @@ return DescriptorMapUpdateResult::Fail(); } - std::lock_guard guard(m_class_info_extractor.GetMutex()); + std::lock_guard guard(m_dynamic_class_info_extractor.GetMutex()); // Fill in our function argument values arguments.GetValueAtIndex(0)->GetScalar() = hash_table.GetTableLoadAddress(); @@ -1558,8 +1649,8 @@ // Write our function arguments into the process so we can run our function if (get_class_info_function->WriteFunctionArguments( - exe_ctx, m_class_info_extractor.GetClassInfoArgs(helper), arguments, - diagnostics)) { + exe_ctx, m_dynamic_class_info_extractor.GetClassInfoArgs(helper), + arguments, diagnostics)) { EvaluateExpressionOptions options; options.SetUnwindOnError(true); options.SetTryAllThreads(false); @@ -1580,8 +1671,8 @@ // Run the function ExpressionResults results = get_class_info_function->ExecuteFunction( - exe_ctx, &m_class_info_extractor.GetClassInfoArgs(helper), options, - diagnostics, return_value); + exe_ctx, &m_dynamic_class_info_extractor.GetClassInfoArgs(helper), + options, diagnostics, return_value); if (results == eExpressionCompleted) { // The result is the number of ClassInfo structures that were filled in @@ -1734,80 +1825,19 @@ const uint32_t num_classes = 128 * 1024; - // Make some types for our arguments - CompilerType clang_uint32_t_type = - ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); - CompilerType clang_void_pointer_type = - ast->GetBasicType(eBasicTypeVoid).GetPointerType(); - - ValueList arguments; - FunctionCaller *get_shared_cache_class_info_function = nullptr; - - if (!m_get_shared_cache_class_info_code) { - Status error; - - // If the inferior objc.dylib has the class_getNameRaw function, - // use that in our jitted expression. Else fall back to the old - // class_getName. - static ConstString g_class_getName_symbol_name("class_getName"); - static ConstString g_class_getNameRaw_symbol_name( - "objc_debug_class_getNameRaw"); - - ConstString class_name_getter_function_name = - HasSymbol(g_class_getNameRaw_symbol_name) - ? g_class_getNameRaw_symbol_name - : g_class_getName_symbol_name; - - // Substitute in the correct class_getName / class_getNameRaw function name, - // concatenate the two parts of our expression text. The format string - // has two %s's, so provide the name twice. - std::string shared_class_expression; - llvm::raw_string_ostream(shared_class_expression) - << llvm::format(g_shared_cache_class_name_funcptr, - class_name_getter_function_name.AsCString(), - class_name_getter_function_name.AsCString()); - - shared_class_expression += g_get_shared_cache_class_info_body; - - auto utility_fn_or_error = exe_ctx.GetTargetRef().CreateUtilityFunction( - std::move(shared_class_expression), g_get_shared_cache_class_info_name, - eLanguageTypeC, exe_ctx); - if (!utility_fn_or_error) { - LLDB_LOG_ERROR( - log, utility_fn_or_error.takeError(), - "Failed to get utility function for implementation lookup: {0}"); - return DescriptorMapUpdateResult::Fail(); - } - - m_get_shared_cache_class_info_code = std::move(*utility_fn_or_error); - - // Next make the function caller for our implementation utility function. - Value value; - value.SetValueType(Value::ValueType::Scalar); - value.SetCompilerType(clang_void_pointer_type); - arguments.PushValue(value); - arguments.PushValue(value); - - value.SetValueType(Value::ValueType::Scalar); - value.SetCompilerType(clang_uint32_t_type); - arguments.PushValue(value); - arguments.PushValue(value); - - get_shared_cache_class_info_function = - m_get_shared_cache_class_info_code->MakeFunctionCaller( - clang_uint32_t_type, arguments, thread_sp, error); - - if (get_shared_cache_class_info_function == nullptr) - return DescriptorMapUpdateResult::Fail(); + UtilityFunction *get_class_info_code = + m_shared_cache_class_info_extractor.GetClassInfoUtilityFunction(exe_ctx); + FunctionCaller *get_shared_cache_class_info_function = + get_class_info_code->GetFunctionCaller(); - } else { - get_shared_cache_class_info_function = - m_get_shared_cache_class_info_code->GetFunctionCaller(); - if (get_shared_cache_class_info_function == nullptr) - return DescriptorMapUpdateResult::Fail(); - arguments = get_shared_cache_class_info_function->GetArgumentValues(); + if (!get_shared_cache_class_info_function) { + LLDB_LOGF(log, "Failed to get implementation lookup function caller."); + return DescriptorMapUpdateResult::Fail(); } + ValueList arguments = + get_shared_cache_class_info_function->GetArgumentValues(); + DiagnosticManager diagnostics; const uint32_t class_info_byte_size = addr_size + 4; @@ -1823,7 +1853,8 @@ return DescriptorMapUpdateResult::Fail(); } - std::lock_guard guard(m_get_shared_cache_class_info_args_mutex); + std::lock_guard guard( + m_shared_cache_class_info_extractor.GetMutex()); // Fill in our function argument values arguments.GetValueAtIndex(0)->GetScalar() = objc_opt_ptr; @@ -1842,8 +1873,8 @@ // Write our function arguments into the process so we can run our function if (get_shared_cache_class_info_function->WriteFunctionArguments( - exe_ctx, m_get_shared_cache_class_info_args, arguments, - diagnostics)) { + exe_ctx, m_shared_cache_class_info_extractor.GetClassInfoArgs(), + arguments, diagnostics)) { EvaluateExpressionOptions options; options.SetUnwindOnError(true); options.SetTryAllThreads(false); @@ -1852,6 +1883,9 @@ options.SetTimeout(process->GetUtilityExpressionTimeout()); options.SetIsForUtilityExpr(true); + CompilerType clang_uint32_t_type = + ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32); + Value return_value; return_value.SetValueType(Value::ValueType::Scalar); return_value.SetCompilerType(clang_uint32_t_type); @@ -1862,8 +1896,8 @@ // Run the function ExpressionResults results = get_shared_cache_class_info_function->ExecuteFunction( - exe_ctx, &m_get_shared_cache_class_info_args, options, diagnostics, - return_value); + exe_ctx, &m_shared_cache_class_info_extractor.GetClassInfoArgs(), + options, diagnostics, return_value); if (results == eExpressionCompleted) { // The result is the number of ClassInfo structures that were filled in