Update LLDB to read a newer format of Objective-C class information from the dyld shared cache

Also, since most of the time the lack of such information is a serious problem that hinders productive debugging, emit an actual user visible warning when this occurs (once per process)

Fixes rdar://19898507

llvm-svn: 230299
This commit is contained in:
Enrico Granata 2015-02-24 02:11:06 +00:00
parent 2ca8c6b9ca
commit 4ae3dda605
2 changed files with 84 additions and 26 deletions

View File

@ -19,12 +19,14 @@
#include "lldb/Core/ClangForward.h"
#include "lldb/Core/ConstString.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Scalar.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/Timer.h"
#include "lldb/Core/ValueObjectVariable.h"
@ -158,7 +160,7 @@ extern "C"
int printf(const char * format, ...);
}
//#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN
// #define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN
#ifdef ENABLE_DEBUG_PRINTF
#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
#else
@ -217,12 +219,14 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
DEBUG_PRINTF ("objc_opt->selopt_offset = %d\n", objc_opt->selopt_offset);
DEBUG_PRINTF ("objc_opt->headeropt_offset = %d\n", objc_opt->headeropt_offset);
DEBUG_PRINTF ("objc_opt->clsopt_offset = %d\n", objc_opt->clsopt_offset);
if (objc_opt->version == 12)
if (objc_opt->version == 12 || objc_opt->version == 13)
{
const objc_clsopt_t* clsopt = (const objc_clsopt_t*)((uint8_t *)objc_opt + objc_opt->clsopt_offset);
const size_t max_class_infos = class_infos_byte_size/sizeof(ClassInfo);
ClassInfo *class_infos = (ClassInfo *)class_infos_ptr;
int32_t zeroOffset = 16;
int32_t invalidEntryOffset = 0;
if (objc_opt->version == 12)
invalidEntryOffset = 16;
const uint8_t *checkbytes = &clsopt->tab[clsopt->mask+1];
const int32_t *offsets = (const int32_t *)(checkbytes + clsopt->capacity);
const objc_classheader_t *classOffsets = (const objc_classheader_t *)(offsets + clsopt->capacity);
@ -234,8 +238,8 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
const int32_t clsOffset = classOffsets[i].clsOffset;
if (clsOffset & 1)
continue; // duplicate
else if (clsOffset == zeroOffset)
continue; // zero offset
else if (clsOffset == invalidEntryOffset)
continue; // invalid offset
if (class_infos && idx < max_class_infos)
{
@ -262,8 +266,8 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr,
const int32_t clsOffset = duplicateClassOffsets[i].clsOffset;
if (clsOffset & 1)
continue; // duplicate
else if (clsOffset == zeroOffset)
continue; // zero offset
else if (clsOffset == invalidEntryOffset)
continue; // invalid offset
if (class_infos && idx < max_class_infos)
{
@ -354,7 +358,8 @@ AppleObjCRuntimeV2::AppleObjCRuntimeV2 (Process *process,
m_loaded_objc_opt (false),
m_non_pointer_isa_cache_ap(NonPointerISACache::CreateInstance(*this,objc_module_sp)),
m_tagged_pointer_vendor_ap(TaggedPointerVendor::CreateInstance(*this,objc_module_sp)),
m_encoding_to_type_sp()
m_encoding_to_type_sp(),
m_noclasses_warning_emitted(false)
{
static const ConstString g_gdb_object_getClass("gdb_object_getClass");
m_has_object_getClass = (objc_module_sp->FindFirstSymbolWithNameAndType(g_gdb_object_getClass, eSymbolTypeCode) != NULL);
@ -1174,7 +1179,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table
return success;
}
void
uint32_t
AppleObjCRuntimeV2::ParseClassInfoArray (const DataExtractor &data, uint32_t num_class_infos)
{
// Parses an array of "num_class_infos" packed ClassInfo structures:
@ -1186,6 +1191,8 @@ AppleObjCRuntimeV2::ParseClassInfoArray (const DataExtractor &data, uint32_t num
// } __attribute__((__packed__));
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
uint32_t num_parsed = 0;
// Iterate through all ClassInfo structures
lldb::offset_t offset = 0;
@ -1211,19 +1218,21 @@ AppleObjCRuntimeV2::ParseClassInfoArray (const DataExtractor &data, uint32_t num
const uint32_t name_hash = data.GetU32(&offset);
ClassDescriptorSP descriptor_sp (new ClassDescriptorV2(*this, isa, NULL));
AddClass (isa, descriptor_sp, name_hash);
num_parsed++;
if (log && log->GetVerbose())
log->Printf("AppleObjCRuntimeV2 added isa=0x%" PRIx64 ", hash=0x%8.8x, name=%s", isa, name_hash,descriptor_sp->GetClassName().AsCString("<unknown>"));
}
}
return num_parsed;
}
bool
AppleObjCRuntimeV2::DescriptorMapUpdateResult
AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
{
Process *process = GetProcess();
if (process == NULL)
return false;
return DescriptorMapUpdateResult::Fail();
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
@ -1232,13 +1241,13 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
ThreadSP thread_sp = process->GetThreadList().GetSelectedThread();
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;
@ -1251,7 +1260,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
const lldb::addr_t objc_opt_ptr = GetSharedCacheReadOnlyAddress();
if (objc_opt_ptr == LLDB_INVALID_ADDRESS)
return false;
return DescriptorMapUpdateResult::Fail();
// Read the total number of classes from the hash table
const uint32_t num_classes = 128*1024;
@ -1259,7 +1268,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
{
if (log)
log->Printf ("No dynamic classes found in gdb_objc_realized_classes_addr.");
return false;
return DescriptorMapUpdateResult::Fail();
}
// Make some types for our arguments
@ -1284,7 +1293,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
if (m_get_shared_cache_class_info_code.get())
function_address.SetOffset(m_get_shared_cache_class_info_code->StartAddress());
else
return false;
return DescriptorMapUpdateResult::Fail();
ValueList arguments;
@ -1310,7 +1319,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
"objc-isa-to-descriptor-shared-cache"));
if (m_get_shared_cache_class_info_function.get() == NULL)
return false;
return DescriptorMapUpdateResult::Fail();
errors.Clear();
@ -1319,7 +1328,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
{
if (log)
log->Printf ("Error compiling function: \"%s\".", errors.GetData());
return false;
return DescriptorMapUpdateResult::Fail();
}
errors.Clear();
@ -1328,7 +1337,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
{
if (log)
log->Printf ("Error Inserting function: \"%s\".", errors.GetData());
return false;
return DescriptorMapUpdateResult::Fail();
}
}
else
@ -1343,7 +1352,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
err);
if (class_infos_addr == LLDB_INVALID_ADDRESS)
return false;
return DescriptorMapUpdateResult::Fail();
Mutex::Locker locker(m_get_shared_cache_class_info_args_mutex);
@ -1353,6 +1362,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
arguments.GetValueAtIndex(2)->GetScalar() = class_infos_byte_size;
bool success = false;
bool any_found = false;
errors.Clear();
@ -1418,8 +1428,8 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
buffer.GetByteSize(),
process->GetByteOrder(),
addr_size);
ParseClassInfoArray (class_infos_data, num_class_infos);
any_found = (ParseClassInfoArray (class_infos_data, num_class_infos) > 0);
}
}
else
@ -1442,7 +1452,7 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapSharedCache()
// Deallocate the memory we allocated for the ClassInfo array
process->DeallocateMemory(class_infos_addr);
return success;
return DescriptorMapUpdateResult(success, any_found);
}
@ -1546,7 +1556,12 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded()
// in the shared cache, but only once per process as this data never
// changes
if (!m_loaded_objc_opt)
UpdateISAToDescriptorMapSharedCache();
{
DescriptorMapUpdateResult shared_cache_update_result = UpdateISAToDescriptorMapSharedCache();
if (!shared_cache_update_result.any_found)
WarnIfNoClassesCached ();
}
}
else
{
@ -1554,6 +1569,20 @@ AppleObjCRuntimeV2::UpdateISAToDescriptorMapIfNeeded()
}
}
void
AppleObjCRuntimeV2::WarnIfNoClassesCached ()
{
if (m_noclasses_warning_emitted)
return;
Debugger &debugger(GetProcess()->GetTarget().GetDebugger());
if (debugger.GetAsyncOutputStream())
{
debugger.GetAsyncOutputStream()->PutCString("warning: could not load any Objective-C class information from the dyld shared cache. This will signficantly reduce the quality of type information available.\n");
m_noclasses_warning_emitted = true;
}
}
// TODO: should we have a transparent_kvo parameter here to say if we
// want to replace the KVO swizzled class with the actual user-level type?

View File

@ -240,6 +240,31 @@ private:
DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendorLegacy);
};
struct DescriptorMapUpdateResult
{
bool update_ran;
bool any_found;
DescriptorMapUpdateResult (bool ran,
bool found)
{
update_ran = ran;
any_found = found;
}
static DescriptorMapUpdateResult
Fail ()
{
return {false, false};
}
static DescriptorMapUpdateResult
Success ()
{
return {true, true};
}
};
AppleObjCRuntimeV2 (Process *process,
const lldb::ModuleSP &objc_module_sp);
@ -258,12 +283,15 @@ private:
bool
UpdateISAToDescriptorMapDynamic(RemoteNXMapTable &hash_table);
void
uint32_t
ParseClassInfoArray (const lldb_private::DataExtractor &data,
uint32_t num_class_infos);
bool
DescriptorMapUpdateResult
UpdateISAToDescriptorMapSharedCache ();
void
WarnIfNoClassesCached ();
lldb::addr_t
GetSharedCacheReadOnlyAddress();
@ -288,6 +316,7 @@ private:
std::unique_ptr<NonPointerISACache> m_non_pointer_isa_cache_ap;
std::unique_ptr<TaggedPointerVendor> m_tagged_pointer_vendor_ap;
EncodingToTypeSP m_encoding_to_type_sp;
bool m_noclasses_warning_emitted;
};
} // namespace lldb_private