We have many radars showing that stepping through C++ code can result in slow steps.

One of the things slowing us down is that ItaniumABILanguageRuntime class doesn't cache vtable to types in a map. This causes us, on every step, for every variable, to read the first pointer in a C++ type that could be dynamic and lookup the symbol, possibly in every symbol file (some symbols files on Darwin can end up having thousands of .o files when using DWARF in .o files, so thousands of .o files are searched each time). 

This fix caches lldb_private::Address (the resolved vtable symbol address in section + offset format) to TypeAndOrName instances inside the one ItaniumABILanguageRuntime in a process. This allows caching of dynamic types and stops us from always doing deep searches in each file.

<rdar://problem/18890778>

llvm-svn: 270488
This commit is contained in:
Greg Clayton 2016-05-23 20:37:24 +00:00
parent 7331cc3774
commit c226778768
4 changed files with 190 additions and 143 deletions

View File

@ -2232,11 +2232,11 @@ public:
/// order.
//------------------------------------------------------------------
uint64_t
ReadUnsignedIntegerFromMemory (lldb::addr_t load_addr,
size_t byte_size,
uint64_t fail_value,
Error &error);
ReadUnsignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, uint64_t fail_value, Error &error);
int64_t
ReadSignedIntegerFromMemory(lldb::addr_t load_addr, size_t byte_size, int64_t fail_value, Error &error);
lldb::addr_t
ReadPointerFromMemory (lldb::addr_t vm_addr,
Error &error);

View File

@ -48,68 +48,26 @@ ItaniumABILanguageRuntime::CouldHaveDynamicValue (ValueObject &in_value)
return in_value.GetCompilerType().IsPossibleDynamicType (NULL, check_cxx, check_objc);
}
bool
ItaniumABILanguageRuntime::GetDynamicTypeAndAddress (ValueObject &in_value,
lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name,
Address &dynamic_address,
Value::ValueType &value_type)
TypeAndOrName
ItaniumABILanguageRuntime::GetTypeInfoFromVTableAddress(ValueObject &in_value, lldb::addr_t original_ptr,
lldb::addr_t vtable_load_addr)
{
// For Itanium, if the type has a vtable pointer in the object, it will be at offset 0
// in the object. That will point to the "address point" within the vtable (not the beginning of the
// vtable.) We can then look up the symbol containing this "address point" and that symbol's name
// demangled will contain the full class name.
// The second pointer above the "address point" is the "offset_to_top". We'll use that to get the
// start of the value object which holds the dynamic type.
//
class_type_or_name.Clear();
value_type = Value::ValueType::eValueTypeScalar;
// Only a pointer or reference type can have a different dynamic and static type:
if (CouldHaveDynamicValue (in_value))
if (m_process && vtable_load_addr != LLDB_INVALID_ADDRESS)
{
// First job, pull out the address at 0 offset from the object.
AddressType address_type;
lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type);
if (original_ptr == LLDB_INVALID_ADDRESS)
return false;
ExecutionContext exe_ctx (in_value.GetExecutionContextRef());
Target *target = exe_ctx.GetTargetPtr();
Process *process = exe_ctx.GetProcessPtr();
char memory_buffer[16];
DataExtractor data(memory_buffer, sizeof(memory_buffer),
process->GetByteOrder(),
process->GetAddressByteSize());
size_t address_byte_size = process->GetAddressByteSize();
Error error;
size_t bytes_read = process->ReadMemory (original_ptr,
memory_buffer,
address_byte_size,
error);
if (!error.Success() || (bytes_read != address_byte_size))
// Find the symbol that contains the "vtable_load_addr" address
Address vtable_addr;
Target &target = m_process->GetTarget();
if (!target.GetSectionLoadList().IsEmpty())
{
return false;
}
lldb::offset_t offset = 0;
lldb::addr_t vtable_address_point = data.GetAddress (&offset);
if (offset == 0)
return false;
// Now find the symbol that contains this address:
SymbolContext sc;
Address address_point_address;
if (target && !target->GetSectionLoadList().IsEmpty())
{
if (target->GetSectionLoadList().ResolveLoadAddress (vtable_address_point, address_point_address))
if (target.GetSectionLoadList().ResolveLoadAddress(vtable_load_addr, vtable_addr))
{
target->GetImages().ResolveSymbolContextForAddress (address_point_address, eSymbolContextSymbol, sc);
// See if we have cached info for this type already
TypeAndOrName type_info = GetDynamicTypeInfo(vtable_addr);
if (type_info)
return type_info;
SymbolContext sc;
target.GetImages().ResolveSymbolContextForAddress(vtable_addr, eSymbolContextSymbol, sc);
Symbol *symbol = sc.symbol;
if (symbol != NULL)
{
@ -124,10 +82,10 @@ ItaniumABILanguageRuntime::GetDynamicTypeAndAddress (ValueObject &in_value,
name);
// We are a C++ class, that's good. Get the class name and look it up:
const char *class_name = name + strlen(vtable_demangled_prefix);
class_type_or_name.SetName (class_name);
type_info.SetName(class_name);
const bool exact_match = true;
TypeList class_types;
uint32_t num_matches = 0;
// First look in the module that the vtable symbol came from
// and look for a single exact match.
@ -141,38 +99,39 @@ ItaniumABILanguageRuntime::GetDynamicTypeAndAddress (ValueObject &in_value,
searched_symbol_files,
class_types);
}
// If we didn't find a symbol, then move on to the entire
// module list in the target and get as many unique matches
// as possible
if (num_matches == 0)
{
num_matches = target->GetImages().FindTypes (sc,
ConstString(class_name),
exact_match,
UINT32_MAX,
searched_symbol_files,
class_types);
num_matches = target.GetImages().FindTypes(sc, ConstString(class_name), exact_match,
UINT32_MAX, searched_symbol_files, class_types);
}
lldb::TypeSP type_sp;
if (num_matches == 0)
{
if (log)
log->Printf("0x%16.16" PRIx64 ": is not dynamic\n", original_ptr);
return false;
return TypeAndOrName();
}
if (num_matches == 1)
{
type_sp = class_types.GetTypeAtIndex(0);
if (log)
log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has dynamic type: uid={0x%" PRIx64 "}, type-name='%s'\n",
original_ptr,
in_value.GetTypeName().AsCString(),
type_sp->GetID(),
type_sp->GetName().GetCString());
class_type_or_name.SetTypeSP(class_types.GetTypeAtIndex(0));
if (type_sp)
{
if (ClangASTContext::IsCXXClassType(type_sp->GetForwardCompilerType()))
{
if (log)
log->Printf("0x%16.16" PRIx64
": static-type = '%s' has dynamic type: uid={0x%" PRIx64
"}, type-name='%s'\n",
original_ptr, in_value.GetTypeName().AsCString(), type_sp->GetID(),
type_sp->GetName().GetCString());
type_info.SetTypeSP(type_sp);
}
}
}
else if (num_matches > 1)
{
@ -207,74 +166,112 @@ ItaniumABILanguageRuntime::GetDynamicTypeAndAddress (ValueObject &in_value,
in_value.GetTypeName().AsCString(),
type_sp->GetID(),
type_sp->GetName().GetCString());
class_type_or_name.SetTypeSP(type_sp);
break;
type_info.SetTypeSP(type_sp);
}
}
}
if (i == num_matches)
if (log && i == num_matches)
{
if (log)
log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has multiple matching dynamic types, didn't find a C++ match\n",
original_ptr,
in_value.GetTypeName().AsCString());
return false;
log->Printf("0x%16.16" PRIx64 ": static-type = '%s' has multiple matching dynamic "
"types, didn't find a C++ match\n",
original_ptr, in_value.GetTypeName().AsCString());
}
}
// There can only be one type with a given name,
// so we've just found duplicate definitions, and this
// one will do as well as any other.
// We don't consider something to have a dynamic type if
// it is the same as the static type. So compare against
// the value we were handed.
if (type_sp)
{
if (ClangASTContext::AreTypesSame (in_value.GetCompilerType(),
type_sp->GetForwardCompilerType ()))
{
// The dynamic type we found was the same type,
// so we don't have a dynamic type here...
return false;
}
// The offset_to_top is two pointers above the address.
Address offset_to_top_address = address_point_address;
int64_t slide = -2 * ((int64_t) target->GetArchitecture().GetAddressByteSize());
offset_to_top_address.Slide (slide);
Error error;
lldb::addr_t offset_to_top_location = offset_to_top_address.GetLoadAddress(target);
size_t bytes_read = process->ReadMemory (offset_to_top_location,
memory_buffer,
address_byte_size,
error);
if (!error.Success() || (bytes_read != address_byte_size))
{
return false;
}
offset = 0;
int64_t offset_to_top = data.GetMaxS64(&offset, process->GetAddressByteSize());
// So the dynamic type is a value that starts at offset_to_top
// above the original address.
lldb::addr_t dynamic_addr = original_ptr + offset_to_top;
if (!target->GetSectionLoadList().ResolveLoadAddress (dynamic_addr, dynamic_address))
{
dynamic_address.SetRawAddress(dynamic_addr);
}
return true;
}
if (type_info)
SetDynamicTypeInfo(vtable_addr, type_info);
return type_info;
}
}
}
}
}
return TypeAndOrName();
}
bool
ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(ValueObject &in_value, lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name, Address &dynamic_address,
Value::ValueType &value_type)
{
// For Itanium, if the type has a vtable pointer in the object, it will be at offset 0
// in the object. That will point to the "address point" within the vtable (not the beginning of the
// vtable.) We can then look up the symbol containing this "address point" and that symbol's name
// demangled will contain the full class name.
// The second pointer above the "address point" is the "offset_to_top". We'll use that to get the
// start of the value object which holds the dynamic type.
//
class_type_or_name.Clear();
value_type = Value::ValueType::eValueTypeScalar;
// Only a pointer or reference type can have a different dynamic and static type:
if (CouldHaveDynamicValue(in_value))
{
// First job, pull out the address at 0 offset from the object.
AddressType address_type;
lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type);
if (original_ptr == LLDB_INVALID_ADDRESS)
return false;
ExecutionContext exe_ctx(in_value.GetExecutionContextRef());
Process *process = exe_ctx.GetProcessPtr();
if (process == nullptr)
return false;
Error error;
const lldb::addr_t vtable_address_point = process->ReadPointerFromMemory(original_ptr, error);
if (!error.Success() || vtable_address_point == LLDB_INVALID_ADDRESS)
{
return false;
}
class_type_or_name = GetTypeInfoFromVTableAddress(in_value, original_ptr, vtable_address_point);
if (class_type_or_name)
{
TypeSP type_sp = class_type_or_name.GetTypeSP();
// There can only be one type with a given name,
// so we've just found duplicate definitions, and this
// one will do as well as any other.
// We don't consider something to have a dynamic type if
// it is the same as the static type. So compare against
// the value we were handed.
if (type_sp)
{
if (ClangASTContext::AreTypesSame(in_value.GetCompilerType(), type_sp->GetForwardCompilerType()))
{
// The dynamic type we found was the same type,
// so we don't have a dynamic type here...
return false;
}
// The offset_to_top is two pointers above the vtable pointer.
const uint32_t addr_byte_size = process->GetAddressByteSize();
const lldb::addr_t offset_to_top_location = vtable_address_point - 2 * addr_byte_size;
// Watch for underflow, offset_to_top_location should be less than vtable_address_point
if (offset_to_top_location >= vtable_address_point)
return false;
const int64_t offset_to_top =
process->ReadSignedIntegerFromMemory(offset_to_top_location, addr_byte_size, INT64_MIN, error);
if (offset_to_top == INT64_MIN)
return false;
// So the dynamic type is a value that starts at offset_to_top
// above the original address.
lldb::addr_t dynamic_addr = original_ptr + offset_to_top;
if (!process->GetTarget().GetSectionLoadList().ResolveLoadAddress(dynamic_addr, dynamic_address))
{
dynamic_address.SetRawAddress(dynamic_addr);
}
return true;
}
}
}
return class_type_or_name.IsEmpty() == false;
}
@ -603,3 +600,21 @@ ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop (lldb::StopInfoSP sto
m_cxx_exception_bp_sp->GetID());
}
TypeAndOrName
ItaniumABILanguageRuntime::GetDynamicTypeInfo(const lldb_private::Address &vtable_addr)
{
std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
DynamicTypeCache::const_iterator pos = m_dynamic_type_map.find(vtable_addr);
if (pos == m_dynamic_type_map.end())
return TypeAndOrName();
else
return pos->second;
}
void
ItaniumABILanguageRuntime::SetDynamicTypeInfo(const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info)
{
std::lock_guard<std::mutex> locker(m_dynamic_type_map_mutex);
m_dynamic_type_map[vtable_addr] = type_info;
}

View File

@ -12,6 +12,8 @@
// C Includes
// C++ Includes
#include <map>
#include <mutex>
#include <vector>
// Other libraries and framework includes
@ -100,9 +102,29 @@ namespace lldb_private {
bool is_internal);
private:
ItaniumABILanguageRuntime(Process *process) : lldb_private::CPPLanguageRuntime(process) { } // Call CreateInstance instead.
lldb::BreakpointSP m_cxx_exception_bp_sp;
typedef std::map<lldb_private::Address, TypeAndOrName> DynamicTypeCache;
ItaniumABILanguageRuntime(Process *process)
: // Call CreateInstance instead.
lldb_private::CPPLanguageRuntime(process),
m_cxx_exception_bp_sp(),
m_dynamic_type_map(),
m_dynamic_type_map_mutex()
{
}
lldb::BreakpointSP m_cxx_exception_bp_sp;
DynamicTypeCache m_dynamic_type_map;
std::mutex m_dynamic_type_map_mutex;
TypeAndOrName
GetTypeInfoFromVTableAddress(ValueObject &in_value, lldb::addr_t original_ptr, lldb::addr_t vtable_addr);
TypeAndOrName
GetDynamicTypeInfo(const lldb_private::Address &vtable_addr);
void
SetDynamicTypeInfo(const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info);
};
} // namespace lldb_private

View File

@ -2524,7 +2524,8 @@ Process::ReadMemoryFromInferior (addr_t addr, void *buf, size_t size, Error &err
}
uint64_t
Process::ReadUnsignedIntegerFromMemory (lldb::addr_t vm_addr, size_t integer_byte_size, uint64_t fail_value, Error &error)
Process::ReadUnsignedIntegerFromMemory(lldb::addr_t vm_addr, size_t integer_byte_size, uint64_t fail_value,
Error &error)
{
Scalar scalar;
if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, false, scalar, error))
@ -2532,6 +2533,15 @@ Process::ReadUnsignedIntegerFromMemory (lldb::addr_t vm_addr, size_t integer_byt
return fail_value;
}
int64_t
Process::ReadSignedIntegerFromMemory(lldb::addr_t vm_addr, size_t integer_byte_size, int64_t fail_value, Error &error)
{
Scalar scalar;
if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, true, scalar, error))
return scalar.SLongLong(fail_value);
return fail_value;
}
addr_t
Process::ReadPointerFromMemory (lldb::addr_t vm_addr, Error &error)
{