Fix an issue where LLDB would detect an empty shared cache - which is legitimate albeit suboptimal - and warn about being unable to fetch ObjC class information, even though class data was actually properly loaded from the dynamic hashmap

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
This commit is contained in:
Enrico Granata 2016-07-21 00:13:40 +00:00
parent 19f802ff68
commit 99bd2de619
2 changed files with 43 additions and 25 deletions

View File

@ -1350,13 +1350,15 @@ AppleObjCRuntimeV2::GetISAHashTablePointer ()
return m_isa_hash_table_ptr; return m_isa_hash_table_ptr;
} }
bool AppleObjCRuntimeV2::DescriptorMapUpdateResult
AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table) AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table)
{ {
Process *process = GetProcess(); Process *process = GetProcess();
if (process == NULL) if (process == NULL)
return false; return DescriptorMapUpdateResult::Fail();
uint32_t num_class_infos = 0;
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
@ -1365,13 +1367,13 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread(); ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
if (!thread_sp) if (!thread_sp)
return false; return DescriptorMapUpdateResult::Fail();
thread_sp->CalculateExecutionContext(exe_ctx); thread_sp->CalculateExecutionContext(exe_ctx);
ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext(); ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
if (!ast) if (!ast)
return false; return DescriptorMapUpdateResult::Fail();
Address function_address; Address function_address;
@ -1387,7 +1389,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
{ {
if (log) if (log)
log->Printf ("No dynamic classes found in gdb_objc_realized_classes."); log->Printf ("No dynamic classes found in gdb_objc_realized_classes.");
return false; return DescriptorMapUpdateResult::Fail();
} }
// Make some types for our arguments // Make some types for our arguments
@ -1425,7 +1427,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
} }
} }
if (!m_get_class_info_code.get()) if (!m_get_class_info_code.get())
return false; return DescriptorMapUpdateResult::Fail();
// Next make the runner function for our implementation utility function. // Next make the runner function for our implementation utility function.
Value value; Value value;
@ -1448,7 +1450,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
{ {
if (log) if (log)
log->Printf("Failed to make function caller for implementation lookup: %s.", error.AsCString()); log->Printf("Failed to make function caller for implementation lookup: %s.", error.AsCString());
return false; return DescriptorMapUpdateResult::Fail();
} }
} }
else else
@ -1462,7 +1464,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
diagnostics.Dump(log); diagnostics.Dump(log);
} }
return false; return DescriptorMapUpdateResult::Fail();
} }
arguments = get_class_info_function->GetArgumentValues(); arguments = get_class_info_function->GetArgumentValues();
} }
@ -1476,7 +1478,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
err); err);
if (class_infos_addr == LLDB_INVALID_ADDRESS) if (class_infos_addr == LLDB_INVALID_ADDRESS)
return false; 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_args_mutex);
@ -1516,7 +1518,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
if (results == eExpressionCompleted) if (results == eExpressionCompleted)
{ {
// The result is the number of ClassInfo structures that were filled in // 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) if (log)
log->Printf("Discovered %u ObjC classes\n",num_class_infos); log->Printf("Discovered %u ObjC classes\n",num_class_infos);
if (num_class_infos > 0) if (num_class_infos > 0)
@ -1555,7 +1557,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
// Deallocate the memory we allocated for the ClassInfo array // Deallocate the memory we allocated for the ClassInfo array
process->DeallocateMemory(class_infos_addr); process->DeallocateMemory(class_infos_addr);
return success; return DescriptorMapUpdateResult(success, num_class_infos);
} }
uint32_t uint32_t
@ -1636,6 +1638,8 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
Error err; Error err;
uint32_t num_class_infos = 0;
const lldb::addr_t objc_opt_ptr = GetSharedCacheReadOnlyAddress(); const lldb::addr_t objc_opt_ptr = GetSharedCacheReadOnlyAddress();
if (objc_opt_ptr == LLDB_INVALID_ADDRESS) if (objc_opt_ptr == LLDB_INVALID_ADDRESS)
@ -1770,7 +1774,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
if (results == eExpressionCompleted) if (results == eExpressionCompleted)
{ {
// The result is the number of ClassInfo structures that were filled in // 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) if (log)
log->Printf("Discovered %u ObjC classes in shared cache\n",num_class_infos); log->Printf("Discovered %u ObjC classes in shared cache\n",num_class_infos);
#ifdef LLDB_CONFIGURATION_DEBUG #ifdef LLDB_CONFIGURATION_DEBUG
@ -1830,7 +1834,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
// Deallocate the memory we allocated for the ClassInfo array // Deallocate the memory we allocated for the ClassInfo array
process->DeallocateMemory(class_infos_addr); process->DeallocateMemory(class_infos_addr);
return DescriptorMapUpdateResult(success, any_found); return DescriptorMapUpdateResult(success, num_class_infos);
} }
bool bool
@ -1927,16 +1931,30 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded()
m_hash_signature.UpdateSignature (hash_table); m_hash_signature.UpdateSignature (hash_table);
// Grab the dynamically loaded objc classes from the hash table in memory // 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 // 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 // in the shared cache, but only once per process as this data never
// changes // changes
if (!m_loaded_objc_opt) 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(); 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 else
m_loaded_objc_opt = true; m_loaded_objc_opt = true;
} }

View File

@ -296,26 +296,26 @@ private:
struct DescriptorMapUpdateResult struct DescriptorMapUpdateResult
{ {
bool update_ran; bool m_update_ran;
bool any_found; uint32_t m_num_found;
DescriptorMapUpdateResult (bool ran, DescriptorMapUpdateResult (bool ran,
bool found) uint32_t found)
{ {
update_ran = ran; m_update_ran = ran;
any_found = found; m_num_found = found;
} }
static DescriptorMapUpdateResult static DescriptorMapUpdateResult
Fail () Fail ()
{ {
return {false, false}; return {false, 0};
} }
static DescriptorMapUpdateResult static DescriptorMapUpdateResult
Success () Success (uint32_t found)
{ {
return {true, true}; return {true, found};
} }
}; };
@ -334,7 +334,7 @@ private:
bool bool
UpdateISAToDescriptorMapFromMemory (RemoteNXMapTable &hash_table); UpdateISAToDescriptorMapFromMemory (RemoteNXMapTable &hash_table);
bool DescriptorMapUpdateResult
UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table); UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table);
uint32_t uint32_t