Fix an issue where LLDB would detect an empty shared cache - which is legitimate...
authorEnrico Granata <egranata@apple.com>
Thu, 21 Jul 2016 00:13:40 +0000 (00:13 +0000)
committerEnrico Granata <egranata@apple.com>
Thu, 21 Jul 2016 00:13:40 +0000 (00:13 +0000)
Only ever warn about missing ObjC runtime class data if one either can't run the expressions to obtain such data, or the total count of classes is below a threshold that makes things sound really suspicious

Fixes rdar://27438500

llvm-svn: 276220

lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h

index e9a799b..d899c1f 100644 (file)
@@ -1350,13 +1350,15 @@ AppleObjCRuntimeV2::GetISAHashTablePointer ()
     return m_isa_hash_table_ptr;
 }
 
-bool
+AppleObjCRuntimeV2::DescriptorMapUpdateResult
 AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table)
 {
     Process *process = GetProcess();
     
     if (process == NULL)
-        return false;
+        return DescriptorMapUpdateResult::Fail();
+    
+    uint32_t num_class_infos = 0;
     
     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
     
@@ -1365,13 +1367,13 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
     ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
     
     if (!thread_sp)
-        return false;
+        return DescriptorMapUpdateResult::Fail();
     
     thread_sp->CalculateExecutionContext(exe_ctx);
     ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
     
     if (!ast)
-        return false;
+        return DescriptorMapUpdateResult::Fail();
 
     Address function_address;
 
@@ -1387,7 +1389,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
     {
         if (log)
             log->Printf ("No dynamic classes found in gdb_objc_realized_classes.");
-        return false;
+        return DescriptorMapUpdateResult::Fail();
     }
     
     // Make some types for our arguments
@@ -1425,7 +1427,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
             }
         }
         if (!m_get_class_info_code.get())
-            return false;
+            return DescriptorMapUpdateResult::Fail();
         
         // Next make the runner function for our implementation utility function.
         Value value;
@@ -1448,7 +1450,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
         {
             if (log)
                 log->Printf("Failed to make function caller for implementation lookup: %s.", error.AsCString());
-            return false;
+            return DescriptorMapUpdateResult::Fail();
         }
     }
     else
@@ -1462,7 +1464,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
                 diagnostics.Dump(log);
             }
 
-            return false;
+            return DescriptorMapUpdateResult::Fail();
         }
         arguments = get_class_info_function->GetArgumentValues();
     }
@@ -1476,7 +1478,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
                                                             err);
     
     if (class_infos_addr == LLDB_INVALID_ADDRESS)
-        return false;
+        return DescriptorMapUpdateResult::Fail();
 
     std::lock_guard<std::mutex> guard(m_get_class_info_args_mutex);
 
@@ -1516,7 +1518,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
         if (results == eExpressionCompleted)
         {
             // The result is the number of ClassInfo structures that were filled in
-            uint32_t num_class_infos = return_value.GetScalar().ULong();
+            num_class_infos = return_value.GetScalar().ULong();
             if (log)
                 log->Printf("Discovered %u ObjC classes\n",num_class_infos);
             if (num_class_infos > 0)
@@ -1555,7 +1557,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
     // Deallocate the memory we allocated for the ClassInfo array
     process->DeallocateMemory(class_infos_addr);
     
-    return success;
+    return DescriptorMapUpdateResult(success, num_class_infos);
 }
 
 uint32_t
@@ -1636,6 +1638,8 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
 
     Error err;
     
+    uint32_t num_class_infos = 0;
+    
     const lldb::addr_t objc_opt_ptr = GetSharedCacheReadOnlyAddress();
     
     if (objc_opt_ptr == LLDB_INVALID_ADDRESS)
@@ -1770,7 +1774,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
         if (results == eExpressionCompleted)
         {
             // The result is the number of ClassInfo structures that were filled in
-            uint32_t num_class_infos = return_value.GetScalar().ULong();
+            num_class_infos = return_value.GetScalar().ULong();
             if (log)
                 log->Printf("Discovered %u ObjC classes in shared cache\n",num_class_infos);
 #ifdef LLDB_CONFIGURATION_DEBUG
@@ -1830,7 +1834,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
     // Deallocate the memory we allocated for the ClassInfo array
     process->DeallocateMemory(class_infos_addr);
     
-    return DescriptorMapUpdateResult(success, any_found);
+    return DescriptorMapUpdateResult(success, num_class_infos);
 }
 
 bool
@@ -1927,16 +1931,30 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded()
         m_hash_signature.UpdateSignature (hash_table);
 
         // Grab the dynamically loaded objc classes from the hash table in memory
-        UpdateISAToDescriptorMapDynamic(hash_table);
+        DescriptorMapUpdateResult dynamic_update_result = UpdateISAToDescriptorMapDynamic(hash_table);
 
         // Now get the objc classes that are baked into the Objective C runtime
         // in the shared cache, but only once per process as this data never
         // changes
         if (!m_loaded_objc_opt)
         {
+            // it is legitimately possible for the shared cache to be empty - in that case, the dynamic hash table
+            // will contain all the class information we need; the situation we're trying to detect is one where
+            // we aren't seeing class information from the runtime - in order to detect that vs. just the shared cache
+            // being empty or sparsely populated, we set an arbitrary (very low) threshold for the number of classes
+            // that we want to see in a "good" scenario - anything below that is suspicious (Foundation alone has thousands
+            // of classes)
+            const uint32_t num_classes_to_warn_at = 500;
+            
             DescriptorMapUpdateResult shared_cache_update_result = UpdateISAToDescriptorMapSharedCache();
-            if (!shared_cache_update_result.any_found)
-                WarnIfNoClassesCached ();
+            
+            // warn if:
+            // - we could not run either expression
+            // - we found fewer than num_classes_to_warn_at classes total
+            if ((false == shared_cache_update_result.m_update_ran) || (false == dynamic_update_result.m_update_ran))
+                WarnIfNoClassesCached();
+            else if (dynamic_update_result.m_num_found + shared_cache_update_result.m_num_found < num_classes_to_warn_at)
+                WarnIfNoClassesCached();
             else
                 m_loaded_objc_opt = true;
         }
index 4b27c74..3bc8ba6 100644 (file)
@@ -296,26 +296,26 @@ private:
     
     struct DescriptorMapUpdateResult
     {
-        bool update_ran;
-        bool any_found;
+        bool m_update_ran;
+        uint32_t m_num_found;
         
         DescriptorMapUpdateResult (bool ran,
-                                   bool found)
+                                   uint32_t found)
         {
-            update_ran = ran;
-            any_found = found;
+            m_update_ran = ran;
+            m_num_found = found;
         }
         
         static DescriptorMapUpdateResult
         Fail ()
         {
-            return {false, false};
+            return {false, 0};
         }
         
         static DescriptorMapUpdateResult
-        Success ()
+        Success (uint32_t found)
         {
-            return {true, true};
+            return {true, found};
         }
     };
     
@@ -334,7 +334,7 @@ private:
     bool
     UpdateISAToDescriptorMapFromMemory (RemoteNXMapTable &hash_table);
     
-    bool
+    DescriptorMapUpdateResult
     UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table);
     
     uint32_t