[lldb] Add consistency between reading the dynamic and shared cache class info

This adds the consistency I promised in D99315 between how we read the
class info from the Objective-C runtime and the shared cache. (NFC)

Differential revision: https://reviews.llvm.org/D99446
This commit is contained in:
Jonas Devlieghere 2021-03-26 14:50:46 -07:00
parent ae7aa9ed15
commit 645764f3aa
2 changed files with 183 additions and 120 deletions

View File

@ -482,9 +482,8 @@ static void RegisterObjCExceptionRecognizer(Process *process);
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 @@ AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoUtilityFunction(
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 @@ lldb::addr_t &
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 @@ AppleObjCRuntimeV2::DynamicClassInfoExtractor::ComputeHelper() const {
return DynamicClassInfoExtractor::gdb_objc_realized_classes;
}
std::unique_ptr<UtilityFunction>
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<UtilityFunction> 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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
// 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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
}
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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
return DescriptorMapUpdateResult::Fail();
}
std::lock_guard<std::mutex> guard(m_class_info_extractor.GetMutex());
std::lock_guard<std::mutex> guard(m_dynamic_class_info_extractor.GetMutex());
// Fill in our function argument values
arguments.GetValueAtIndex(0)->GetScalar() = hash_table.GetTableLoadAddress();
@ -1558,8 +1649,8 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
// 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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
// 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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() {
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();
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();
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();
} 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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() {
return DescriptorMapUpdateResult::Fail();
}
std::lock_guard<std::mutex> guard(m_get_shared_cache_class_info_args_mutex);
std::lock_guard<std::mutex> 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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() {
// 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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() {
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 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() {
// 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

View File

@ -293,19 +293,30 @@ private:
}
};
/// 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 @@ private:
UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx,
Helper helper);
lldb::addr_t &GetClassInfoArgs(Helper helper);
std::mutex &GetMutex() { return m_mutex; }
private:
std::unique_ptr<UtilityFunction>
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;
/// Helper to read class info using the gdb_objc_realized_classes.
struct gdb_objc_realized_classes_helper {
std::unique_ptr<UtilityFunction> utility_function;
lldb::addr_t args = LLDB_INVALID_ADDRESS;
};
/// Helper to read class info using objc_copyRealizedClassList.
struct objc_copyRealizedClassList_helper {
std::unique_ptr<UtilityFunction> 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) {}
UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx);
lldb::addr_t &GetClassInfoArgs() { return m_args; }
std::mutex &GetMutex() { return m_mutex; }
private:
std::unique_ptr<UtilityFunction>
GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx);
std::unique_ptr<UtilityFunction> m_utility_function;
lldb::addr_t m_args = LLDB_INVALID_ADDRESS;
std::mutex m_mutex;
/// Utility function to read class info using gdb_objc_realized_classes.
std::unique_ptr<UtilityFunction> m_get_class_info_code;
lldb::addr_t m_get_class_info_args = LLDB_INVALID_ADDRESS;
/// Utility function to read class info using objc_copyRealizedClassList.
std::unique_ptr<UtilityFunction> m_get_class_info2_code;
lldb::addr_t m_get_class_info2_args = LLDB_INVALID_ADDRESS;
};
AppleObjCRuntimeV2(Process *process, const lldb::ModuleSP &objc_module_sp);
@ -383,11 +415,8 @@ private:
lldb::ModuleSP m_objc_module_sp;
DynamicClassInfoExtractor m_class_info_extractor;
std::unique_ptr<UtilityFunction> 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<DeclVendor> m_decl_vendor_up;
lldb::addr_t m_tagged_pointer_obfuscator;