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;
}
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;
}

View 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