This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG077aa102534a: [lldb] Support lazily named classes in the
Objective-C classes (authored by JDevlieghere).
Herald added a project: LLDB.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D99315/new/
https://reviews.llvm.org/D99315
Files:
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -293,6 +293,50 @@
}
};
+ /// 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 {
+ public:
+ DynamicClassInfoExtractor(AppleObjCRuntimeV2 &runtime)
+ : m_runtime(runtime) {}
+
+ enum Helper { gdb_objc_realized_classes, objc_copyRealizedClassList };
+
+ /// Compute which helper to use. Prefer objc_copyRealizedClassList if it's
+ /// available and it's safe to call (i.e. dyld is fully initialized). Use
+ /// gdb_objc_realized_classes otherwise.
+ Helper ComputeHelper() const;
+
+ 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;
+ 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);
ObjCISA GetPointerISA(ObjCISA isa);
@@ -301,6 +345,12 @@
bool UpdateISAToDescriptorMapFromMemory(RemoteNXMapTable &hash_table);
+ /// Update the generation count of realized classes. This is not an exact
+ /// count but rather a value that is incremented when new classes are realized
+ /// or destroyed. Unlike the count in gdb_objc_realized_classes, it will
+ /// change when lazily named classes get realized.
+ bool RealizedClassGenerationCountChanged();
+
DescriptorMapUpdateResult
UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table);
@@ -320,6 +370,8 @@
bool GetCFBooleanValuesIfNeeded();
+ bool HasSymbol(ConstString Name);
+
NonPointerISACache *GetNonPointerIsaCache() {
if (!m_non_pointer_isa_cache_up)
m_non_pointer_isa_cache_up.reset(
@@ -331,9 +383,7 @@
lldb::ModuleSP m_objc_module_sp;
- std::unique_ptr<UtilityFunction> m_get_class_info_code;
- lldb::addr_t m_get_class_info_args;
- std::mutex m_get_class_info_args_mutex;
+ 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;
@@ -344,12 +394,14 @@
lldb::addr_t m_isa_hash_table_ptr;
HashTableSignature m_hash_signature;
bool m_has_object_getClass;
+ bool m_has_objc_copyRealizedClassList;
bool m_loaded_objc_opt;
std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_up;
std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_up;
EncodingToTypeSP m_encoding_to_type_sp;
bool m_noclasses_warning_emitted;
llvm::Optional<std::pair<lldb::addr_t, lldb::addr_t>> m_CFBoolean_values;
+ uint64_t m_realized_class_generation_count;
};
} // namespace lldb_private
Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
===================================================================
--- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -39,6 +39,7 @@
#include "lldb/Symbol/TypeList.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/Process.h"
@@ -163,6 +164,72 @@
)";
+static const char *g_get_dynamic_class_info2_name =
+ "__lldb_apple_objc_v2_get_dynamic_class_info2";
+
+static const char *g_get_dynamic_class_info2_body = R"(
+
+extern "C" {
+ int printf(const char * format, ...);
+ void free(void *ptr);
+ Class* objc_copyRealizedClassList(unsigned int *outCount);
+ const char* objc_debug_class_getNameRaw(Class cls);
+}
+
+#define DEBUG_PRINTF(fmt, ...) if (should_log) printf(fmt, ## __VA_ARGS__)
+
+struct ClassInfo
+{
+ Class isa;
+ uint32_t hash;
+} __attribute__((__packed__));
+
+uint32_t
+__lldb_apple_objc_v2_get_dynamic_class_info2(void *gdb_objc_realized_classes_ptr,
+ void *class_infos_ptr,
+ uint32_t class_infos_byte_size,
+ uint32_t should_log)
+{
+ DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr);
+ DEBUG_PRINTF ("class_infos_byte_size = %u\n", class_infos_byte_size);
+
+ const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
+ DEBUG_PRINTF ("max_class_infos = %u\n", max_class_infos);
+
+ ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
+
+ uint32_t count = 0;
+ Class* realized_class_list = objc_copyRealizedClassList(&count);
+
+ uint32_t idx = 0;
+ for (uint32_t i=0; i<=count; ++i)
+ {
+ if (idx < max_class_infos)
+ {
+ Class isa = realized_class_list[i];
+ const char *name_ptr = objc_debug_class_getNameRaw(isa);
+ const char *s = name_ptr;
+ uint32_t h = 5381;
+ for (unsigned char c = *s; c; c = *++s)
+ h = ((h << 5) + h) + c;
+ class_infos[idx].hash = h;
+ class_infos[idx].isa = isa;
+ DEBUG_PRINTF ("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name_ptr);
+ }
+ idx++;
+ }
+
+ if (idx < max_class_infos)
+ {
+ class_infos[idx].isa = NULL;
+ class_infos[idx].hash = 0;
+ }
+
+ free(realized_class_list);
+ return count;
+}
+)";
+
// We'll substitute in class_getName or class_getNameRaw depending
// on which is present.
static const char *g_shared_cache_class_name_funcptr = R"(
@@ -415,22 +482,23 @@
AppleObjCRuntimeV2::AppleObjCRuntimeV2(Process *process,
const ModuleSP &objc_module_sp)
: AppleObjCRuntime(process), m_objc_module_sp(objc_module_sp),
- m_get_class_info_code(), m_get_class_info_args(LLDB_INVALID_ADDRESS),
- m_get_class_info_args_mutex(), m_get_shared_cache_class_info_code(),
+ 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_tagged_pointer_obfuscator(LLDB_INVALID_ADDRESS),
m_isa_hash_table_ptr(LLDB_INVALID_ADDRESS), m_hash_signature(),
- m_has_object_getClass(false), m_loaded_objc_opt(false),
- m_non_pointer_isa_cache_up(),
+ m_has_object_getClass(false), m_has_objc_copyRealizedClassList(false),
+ m_loaded_objc_opt(false), m_non_pointer_isa_cache_up(),
m_tagged_pointer_vendor_up(
TaggedPointerVendorV2::CreateInstance(*this, objc_module_sp)),
m_encoding_to_type_sp(), m_noclasses_warning_emitted(false),
- m_CFBoolean_values() {
+ m_CFBoolean_values(), m_realized_class_generation_count(0) {
static const ConstString g_gdb_object_getClass("gdb_object_getClass");
- m_has_object_getClass =
- (objc_module_sp->FindFirstSymbolWithNameAndType(
- g_gdb_object_getClass, eSymbolTypeCode) != nullptr);
+ m_has_object_getClass = HasSymbol(g_gdb_object_getClass);
+ static const ConstString g_objc_copyRealizedClassList(
+ "objc_copyRealizedClassList");
+ m_has_objc_copyRealizedClassList = HasSymbol(g_objc_copyRealizedClassList);
+
RegisterObjCExceptionRecognizer(process);
}
@@ -1291,6 +1359,107 @@
return m_isa_hash_table_ptr;
}
+std::unique_ptr<UtilityFunction>
+AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoUtilityFunctionImpl(
+ ExecutionContext &exe_ctx, std::string code, std::string name) {
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+
+ LLDB_LOG(log, "Creating utility function {0}", name);
+
+ TypeSystemClang *ast =
+ ScratchTypeSystemClang::GetForTarget(exe_ctx.GetTargetRef());
+ if (!ast)
+ return {};
+
+ auto utility_fn_or_error = exe_ctx.GetTargetRef().CreateUtilityFunction(
+ std::move(code), std::move(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 {};
+ }
+
+ // Make some types for our arguments.
+ CompilerType clang_uint32_t_type =
+ ast->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32);
+ CompilerType clang_void_pointer_type =
+ ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+ // Make the runner function 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::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();
+ }
+ 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();
+ }
+ };
+}
+
+lldb::addr_t &
+AppleObjCRuntimeV2::DynamicClassInfoExtractor::GetClassInfoArgs(Helper helper) {
+ switch (helper) {
+ case gdb_objc_realized_classes:
+ return m_get_class_info_args;
+ case objc_copyRealizedClassList:
+ return m_get_class_info2_args;
+ }
+}
+
+AppleObjCRuntimeV2::DynamicClassInfoExtractor::Helper
+AppleObjCRuntimeV2::DynamicClassInfoExtractor::ComputeHelper() const {
+ if (!m_runtime.m_has_objc_copyRealizedClassList)
+ return DynamicClassInfoExtractor::gdb_objc_realized_classes;
+
+ if (Process *process = m_runtime.GetProcess()) {
+ if (DynamicLoader *loader = process->GetDynamicLoader()) {
+ if (loader->IsFullyInitialized())
+ return DynamicClassInfoExtractor::objc_copyRealizedClassList;
+ }
+ }
+
+ return DynamicClassInfoExtractor::gdb_objc_realized_classes;
+}
+
AppleObjCRuntimeV2::DescriptorMapUpdateResult
AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
RemoteNXMapTable &hash_table) {
@@ -1323,65 +1492,37 @@
Status err;
+ // Compute which helper we're going to use for this update.
+ const DynamicClassInfoExtractor::Helper helper =
+ m_class_info_extractor.ComputeHelper();
+
// Read the total number of classes from the hash table
- const uint32_t num_classes = hash_table.GetCount();
+ const uint32_t num_classes =
+ helper == DynamicClassInfoExtractor::gdb_objc_realized_classes
+ ? hash_table.GetCount()
+ : m_realized_class_generation_count;
if (num_classes == 0) {
- LLDB_LOGF(log, "No dynamic classes found in gdb_objc_realized_classes.");
+ LLDB_LOGF(log, "No dynamic classes found.");
return DescriptorMapUpdateResult::Success(0);
}
- // 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_class_info_function = nullptr;
-
- if (!m_get_class_info_code) {
- auto utility_fn_or_error = GetTargetRef().CreateUtilityFunction(
- g_get_dynamic_class_info_body, g_get_dynamic_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_class_info_code = std::move(*utility_fn_or_error);
-
- // Next make the runner function 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);
+ UtilityFunction *get_class_info_code =
+ m_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();
+ }
- Status error;
- get_class_info_function = m_get_class_info_code->MakeFunctionCaller(
- clang_uint32_t_type, arguments, thread_sp, error);
+ FunctionCaller *get_class_info_function =
+ get_class_info_code->GetFunctionCaller();
- if (error.Fail()) {
- LLDB_LOGF(log,
- "Failed to make function caller for implementation lookup: %s.",
- error.AsCString());
- return DescriptorMapUpdateResult::Fail();
- }
- } else {
- get_class_info_function = m_get_class_info_code->GetFunctionCaller();
- if (!get_class_info_function) {
- LLDB_LOGF(log, "Failed to get implementation lookup function caller.");
- return DescriptorMapUpdateResult::Fail();
- }
- arguments = get_class_info_function->GetArgumentValues();
+ if (!get_class_info_function) {
+ LLDB_LOGF(log, "Failed to get implementation lookup function caller.");
+ return DescriptorMapUpdateResult::Fail();
}
+ ValueList arguments = get_class_info_function->GetArgumentValues();
+
DiagnosticManager diagnostics;
const uint32_t class_info_byte_size = addr_size + 4;
@@ -1397,7 +1538,7 @@
return DescriptorMapUpdateResult::Fail();
}
- std::lock_guard<std::mutex> guard(m_get_class_info_args_mutex);
+ std::lock_guard<std::mutex> guard(m_class_info_extractor.GetMutex());
// Fill in our function argument values
arguments.GetValueAtIndex(0)->GetScalar() = hash_table.GetTableLoadAddress();
@@ -1417,7 +1558,8 @@
// Write our function arguments into the process so we can run our function
if (get_class_info_function->WriteFunctionArguments(
- exe_ctx, m_get_class_info_args, arguments, diagnostics)) {
+ exe_ctx, m_class_info_extractor.GetClassInfoArgs(helper), arguments,
+ diagnostics)) {
EvaluateExpressionOptions options;
options.SetUnwindOnError(true);
options.SetTryAllThreads(false);
@@ -1426,6 +1568,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);
@@ -1435,12 +1580,13 @@
// Run the function
ExpressionResults results = get_class_info_function->ExecuteFunction(
- exe_ctx, &m_get_class_info_args, options, diagnostics, return_value);
+ exe_ctx, &m_class_info_extractor.GetClassInfoArgs(helper), options,
+ diagnostics, return_value);
if (results == eExpressionCompleted) {
// The result is the number of ClassInfo structures that were filled in
num_class_infos = return_value.GetScalar().ULong();
- LLDB_LOGF(log, "Discovered %u ObjC classes\n", num_class_infos);
+ LLDB_LOG(log, "Discovered {0} Objective-C classes", num_class_infos);
if (num_class_infos > 0) {
// Read the ClassInfo structures
DataBufferHeap buffer(num_class_infos * class_info_byte_size, 0);
@@ -1539,6 +1685,17 @@
return num_parsed;
}
+bool AppleObjCRuntimeV2::HasSymbol(ConstString Name) {
+ if (!m_objc_module_sp)
+ return false;
+ if (const Symbol *symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(
+ Name, lldb::eSymbolTypeCode)) {
+ if (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())
+ return true;
+ }
+ return false;
+}
+
AppleObjCRuntimeV2::DescriptorMapUpdateResult
AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache() {
Process *process = GetProcess();
@@ -1595,21 +1752,11 @@
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 = g_class_getName_symbol_name;
- ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process);
- if (objc_runtime) {
- for (lldb::ModuleSP mod_sp : process->GetTarget().GetImages().Modules()) {
- if (objc_runtime->IsModuleObjCLibrary(mod_sp)) {
- const Symbol *symbol = mod_sp->FindFirstSymbolWithNameAndType(
- g_class_getNameRaw_symbol_name, lldb::eSymbolTypeCode);
- if (symbol &&
- (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())) {
- class_name_getter_function_name = g_class_getNameRaw_symbol_name;
- }
- }
- }
- }
+ 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
@@ -1721,8 +1868,8 @@
if (results == eExpressionCompleted) {
// The result is the number of ClassInfo structures that were filled in
num_class_infos = return_value.GetScalar().ULong();
- LLDB_LOGF(log, "Discovered %u ObjC classes in shared cache\n",
- num_class_infos);
+ LLDB_LOG(log, "Discovered {0} Objective-C classes in the shared cache",
+ num_class_infos);
assert(num_class_infos <= num_classes);
if (num_class_infos > 0) {
if (num_class_infos > num_classes) {
@@ -1850,12 +1997,17 @@
// map, whether it was successful or not.
m_isa_to_descriptor_stop_id = process->GetStopID();
- if (!m_hash_signature.NeedsUpdate(process, this, hash_table))
+ // Ask the runtime is the realized class generation count changed. Unlike
+ // the hash table, this accounts for lazily named classes.
+ const bool class_count_changed = RealizedClassGenerationCountChanged();
+
+ if (!m_hash_signature.NeedsUpdate(process, this, hash_table) &&
+ !class_count_changed)
return;
m_hash_signature.UpdateSignature(hash_table);
- // Grab the dynamically loaded objc classes from the hash table in memory
+ // Grab the dynamically loaded Objective-C classes from memory.
DescriptorMapUpdateResult dynamic_update_result =
UpdateISAToDescriptorMapDynamic(hash_table);
@@ -1903,6 +2055,35 @@
}
}
+bool AppleObjCRuntimeV2::RealizedClassGenerationCountChanged() {
+ Process *process = GetProcess();
+ if (!process)
+ return false;
+
+ Status error;
+ uint64_t objc_debug_realized_class_generation_count =
+ ExtractRuntimeGlobalSymbol(
+ process, ConstString("objc_debug_realized_class_generation_count"),
+ GetObjCModule(), error);
+ if (error.Fail())
+ return false;
+
+ if (m_realized_class_generation_count ==
+ objc_debug_realized_class_generation_count)
+ return false;
+
+ Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_TYPES));
+ LLDB_LOG(log,
+ "objc_debug_realized_class_generation_count changed from {0} to {1}",
+ m_realized_class_generation_count,
+ objc_debug_realized_class_generation_count);
+
+ m_realized_class_generation_count =
+ objc_debug_realized_class_generation_count;
+
+ return true;
+}
+
static bool DoesProcessHaveSharedCache(Process &process) {
PlatformSP platform_sp = process.GetTarget().GetPlatform();
if (!platform_sp)
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits