JDevlieghere created this revision.
JDevlieghere added reviewers: friss, aprantl, jingham.
JDevlieghere requested review of this revision.

Generic classes in Swift have their name instantiated on request, since
the vast majority never need it, and it just wastes time and memory.
This results in LLDB being unable to determine the dynamic type of these
Swift objects.

The main issues is that lazily named classes are not added to the
gdb_objc_realized_classes hashtable. This means the class count in the
table doesn't change when a class is realized and LLDB doesn't know it
needs to re-parse the class info. But even if it did, the classes are
not in the hash table.

The first change in this patch is that we read
objc_debug_realized_class_generation_count and re-parse the class info
when the count changes.

The second change in this patch is that we use
objc_copyRealizedClassList (if available) to get all realized classes
from the runtime.

Unfortunately, objc_copyRealizedClassList calls _dyld_objc_class_count
in its implementation. As we know, the Objective-C parsing code might
get called before dyld is fully initialized, resulting in crashes or
even a stranded lock. Therefore we only use objc_copyRealizedClassList
when we know it's safe to do so by checking libSystemInitialized in
dyld_all_image_infos.

As a result, it's possible that the first time we read the Objective-C
runtime we are forced to use gdb_objc_realized_classes. This should be
fine, as there should be no lazily named classes at this point.
Subsequent queries will detect the change in realized class generation
count and use objc_copyRealizedClassList.

This patch keeps the old behavior when objc_copyRealizedClassList or
objc_debug_realized_class_generation_count are not available.


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,45 @@
     }
   };
 
+  /// 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 GetClassInfo {
+  public:
+    enum Helper { gdb_objc_realized_classes, objc_copyRealizedClassList };
+
+    UtilityFunction *GetClassInfoUtilityFunction(ExecutionContext &exe_ctx,
+                                                 Helper helper);
+    lldb::addr_t &GetClassInfoArgs(Helper helper);
+    std::mutex &GetMutex() { return m_mutex; }
+
+    /// 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.
+    static Helper ComputeHelper(AppleObjCRuntimeV2 &runtime);
+
+  private:
+    std::unique_ptr<UtilityFunction>
+    GetClassInfoUtilityFunctionImpl(ExecutionContext &exe_ctx, std::string code,
+                                    std::string name);
+
+    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 +340,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 +365,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 +378,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;
+  GetClassInfo m_get_class_info;
 
   std::unique_ptr<UtilityFunction> m_get_shared_cache_class_info_code;
   lldb::addr_t m_get_shared_cache_class_info_args;
@@ -344,12 +389,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;
+
+    unsigned int count = 0;
+    Class* realized_class_list = objc_copyRealizedClassList(&count);
+
+    uint32_t idx = 0;
+    for (unsigned 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_get_class_info(), 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,106 @@
   return m_isa_hash_table_ptr;
 }
 
+std::unique_ptr<UtilityFunction>
+AppleObjCRuntimeV2::GetClassInfo::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::GetClassInfo::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::GetClassInfo::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::GetClassInfo::Helper
+AppleObjCRuntimeV2::GetClassInfo::ComputeHelper(AppleObjCRuntimeV2 &runtime) {
+  if (!runtime.m_has_objc_copyRealizedClassList)
+    return GetClassInfo::gdb_objc_realized_classes;
+
+  if (Process *process = runtime.GetProcess()) {
+    if (DynamicLoader *loader = process->GetDynamicLoader()) {
+      if (loader->IsSafeToCallAPI())
+        return GetClassInfo::objc_copyRealizedClassList;
+    }
+  }
+
+  return GetClassInfo::gdb_objc_realized_classes;
+}
+
 AppleObjCRuntimeV2::DescriptorMapUpdateResult
 AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(
     RemoteNXMapTable &hash_table) {
@@ -1323,65 +1491,34 @@
 
   Status err;
 
+  const GetClassInfo::Helper helper = GetClassInfo::ComputeHelper(*this);
+
   // Read the total number of classes from the hash table
-  const uint32_t num_classes = hash_table.GetCount();
+  const uint32_t num_classes = helper == GetClassInfo::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_get_class_info.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 +1534,7 @@
     return DescriptorMapUpdateResult::Fail();
   }
 
-  std::lock_guard<std::mutex> guard(m_get_class_info_args_mutex);
+  std::lock_guard<std::mutex> guard(m_get_class_info.GetMutex());
 
   // Fill in our function argument values
   arguments.GetValueAtIndex(0)->GetScalar() = hash_table.GetTableLoadAddress();
@@ -1417,7 +1554,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_get_class_info.GetClassInfoArgs(helper), arguments,
+          diagnostics)) {
     EvaluateExpressionOptions options;
     options.SetUnwindOnError(true);
     options.SetTryAllThreads(false);
@@ -1426,6 +1564,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 +1576,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_get_class_info.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_LOGF(log, "Discovered %u 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 +1681,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 +1748,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,7 +1864,7 @@
     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",
+      LLDB_LOGF(log, "Discovered %u Objective-C classes in the shared cache",
                 num_class_infos);
       assert(num_class_infos <= num_classes);
       if (num_class_infos > 0) {
@@ -1850,12 +1993,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 +2051,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
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to