476 lines
21 KiB
C++
Raw Normal View History

//===-- ItaniumABILanguageRuntime.cpp --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ItaniumABILanguageRuntime.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Core/ConstString.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/ValueObject.h"
#include "lldb/Core/ValueObjectMemory.h"
#include "lldb/Symbol/ClangASTContext.h"
<rdar://problem/11757916> Make breakpoint setting by file and line much more efficient by only looking for inlined breakpoint locations if we are setting a breakpoint in anything but a source implementation file. Implementing this complex for a many reasons. Turns out that parsing compile units lazily had some issues with respect to how we need to do things with DWARF in .o files. So the fixes in the checkin for this makes these changes: - Add a new setting called "target.inline-breakpoint-strategy" which can be set to "never", "always", or "headers". "never" will never try and set any inlined breakpoints (fastest). "always" always looks for inlined breakpoint locations (slowest, but most accurate). "headers", which is the default setting, will only look for inlined breakpoint locations if the breakpoint is set in what are consudered to be header files, which is realy defined as "not in an implementation source file". - modify the breakpoint setting by file and line to check the current "target.inline-breakpoint-strategy" setting and act accordingly - Modify compile units to be able to get their language and other info lazily. This allows us to create compile units from the debug map and not have to fill all of the details in, and then lazily discover this information as we go on debuggging. This is needed to avoid parsing all .o files when setting breakpoints in implementation only files (no inlines). Otherwise we would need to parse the .o file, the object file (mach-o in our case) and the symbol file (DWARF in the object file) just to see what the compile unit was. - modify the "SymbolFileDWARFDebugMap" to subclass lldb_private::Module so that the virtual "GetObjectFile()" and "GetSymbolVendor()" functions can be intercepted when the .o file contenst are later lazilly needed. Prior to this fix, when we first instantiated the "SymbolFileDWARFDebugMap" class, we would also make modules, object files and symbol files for every .o file in the debug map because we needed to fix up the sections in the .o files with information that is in the executable debug map. Now we lazily do this in the DebugMapModule::GetObjectFile() Cleaned up header includes a bit as well. llvm-svn: 162860
2012-08-29 21:13:06 +00:00
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/TypeList.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include <vector>
using namespace lldb;
using namespace lldb_private;
static const char *pluginName = "ItaniumABILanguageRuntime";
static const char *pluginDesc = "Itanium ABI for the C++ language";
static const char *pluginShort = "language.itanium";
static const char *vtable_demangled_prefix = "vtable for ";
bool
ItaniumABILanguageRuntime::CouldHaveDynamicValue (ValueObject &in_value)
{
return ClangASTContext::IsPossibleDynamicType(in_value.GetClangAST(), in_value.GetClangType(), NULL,
true, // check for C++
false); // do not check for ObjC
}
bool
ItaniumABILanguageRuntime::GetDynamicTypeAndAddress (ValueObject &in_value,
lldb::DynamicValueType use_dynamic,
TypeAndOrName &class_type_or_name,
Address &dynamic_address)
{
// 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();
// 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;
Redesign of the interaction between Python and frozen objects: - introduced two new classes ValueObjectConstResultChild and ValueObjectConstResultImpl: the first one is a ValueObjectChild obtained from a ValueObjectConstResult, the second is a common implementation backend for VOCR and VOCRCh of method calls meant to read through pointers stored in frozen objects ; now such reads transparently move from host to target as required - as a consequence of the above, removed code that made target-memory copies of expression results in several places throughout LLDB, and also removed code that enabled to recognize an expression result VO as such - introduced a new GetPointeeData() method in ValueObject that lets you read a given amount of objects of type T from a VO representing a T* or T[], and doing dereferences transparently in private layer it returns a DataExtractor ; in public layer it returns an instance of a newly created lldb::SBData - as GetPointeeData() does the right thing for both frozen and non-frozen ValueObject's, reimplemented ReadPointedString() to use it en lieu of doing the raw read itself - introduced a new GetData() method in ValueObject that lets you get a copy of the data that backs the ValueObject (for pointers, this returns the address without any previous dereferencing steps ; for arrays it actually reads the whole chunk of memory) in public layer this returns an SBData, just like GetPointeeData() - introduced a new CreateValueFromData() method in SBValue that lets you create a new SBValue from a chunk of data wrapped in an SBData the limitation to remember for this kind of SBValue is that they have no address: extracting the address-of for these objects (with any of GetAddress(), GetLoadAddress() and AddressOf()) will return invalid values - added several tests to check that "p"-ing objects (STL classes, char* and char[]) will do the right thing Solved a bug where global pointers to global variables were not dereferenced correctly for display New target setting "max-string-summary-length" gives the maximum number of characters to show in a string when summarizing it, instead of the hardcoded 128 Solved a bug where the summary for char[] and char* would not be shown if the ValueObject's were dumped via the "p" command Removed m_pointers_point_to_load_addrs from ValueObject. Introduced a new m_address_type_of_children, which each ValueObject can set to tell the address type of any pointers and/or references it creates. In the current codebase, this is load address most of the time (the only notable exception being file addresses that generate file address children UNLESS we have a live process) Updated help text for summary-string Fixed an issue in STL formatters where std::stlcontainer::iterator would match the container's synthetic children providers Edited the syntax and help for some commands to have proper argument types llvm-svn: 139160
2011-09-06 19:20:51 +00:00
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))
{
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))
{
target->GetImages().ResolveSymbolContextForAddress (address_point_address, eSymbolContextSymbol, sc);
Symbol *symbol = sc.symbol;
if (symbol != NULL)
{
const char *name = symbol->GetMangled().GetDemangledName().AsCString();
if (strstr(name, vtable_demangled_prefix) == name)
{
LogSP log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
if (log)
log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has vtable symbol '%s'\n",
original_ptr,
in_value.GetTypeName().GetCString(),
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);
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.
if (sc.module_sp)
{
num_matches = sc.module_sp->FindTypes (sc,
ConstString(class_name),
exact_match,
1,
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,
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;
}
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));
}
else if (num_matches > 1)
{
size_t i;
if (log)
{
for (i = 0; i < num_matches; i++)
{
type_sp = class_types.GetTypeAtIndex(i);
if (type_sp)
{
if (log)
log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has multiple matching dynamic types: uid={0x%" PRIx64 "}, type-name='%s'\n",
original_ptr,
in_value.GetTypeName().AsCString(),
type_sp->GetID(),
type_sp->GetName().GetCString());
}
}
}
for (i = 0; i < num_matches; i++)
{
type_sp = class_types.GetTypeAtIndex(i);
if (type_sp)
{
if (ClangASTContext::IsCXXClassType(type_sp->GetClangFullType()))
{
if (log)
log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has multiple matching dynamic types, picking this one: 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(type_sp);
break;
}
}
}
if (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;
}
}
// 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)
{
clang::ASTContext *in_ast_ctx = in_value.GetClangAST ();
clang::ASTContext *this_ast_ctx = type_sp->GetClangAST ();
if (in_ast_ctx == this_ast_ctx)
{
if (ClangASTContext::AreTypesSame (in_ast_ctx,
in_value.GetClangType(),
type_sp->GetClangFullType()))
{
// 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;
}
}
}
}
}
}
return class_type_or_name.IsEmpty() == false;
}
bool
ItaniumABILanguageRuntime::IsVTableName (const char *name)
{
if (name == NULL)
return false;
// Can we maybe ask Clang about this?
if (strstr (name, "_vptr$") == name)
return true;
else
return false;
}
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------
LanguageRuntime *
ItaniumABILanguageRuntime::CreateInstance (Process *process, lldb::LanguageType language)
{
// FIXME: We have to check the process and make sure we actually know that this process supports
// the Itanium ABI.
if (language == eLanguageTypeC_plus_plus)
return new ItaniumABILanguageRuntime (process);
else
return NULL;
}
void
ItaniumABILanguageRuntime::Initialize()
{
PluginManager::RegisterPlugin (pluginName,
pluginDesc,
CreateInstance);
}
void
ItaniumABILanguageRuntime::Terminate()
{
PluginManager::UnregisterPlugin (CreateInstance);
}
//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
const char *
ItaniumABILanguageRuntime::GetPluginName()
{
return pluginName;
}
const char *
ItaniumABILanguageRuntime::GetShortPluginName()
{
return pluginShort;
}
uint32_t
ItaniumABILanguageRuntime::GetPluginVersion()
{
return 1;
}
// This is an array of symbol names to use in setting exception breakpoints. The names are laid out:
//
// catch_names, general_throw_names, throw_names_for_use_in_expressions
//
// Then you can use the following constants to pick out the part of the array you want to pass to the breakpoint
// resolver.
static const char *exception_names[] = { "__cxa_begin_catch", "__cxa_throw", "__cxa_rethrow", "__cxa_allocate_exception"};
static const int num_exception_names = sizeof (exception_names)/sizeof (char *);
static const int num_catch_names = 1;
static const int num_throw_names = num_exception_names - num_catch_names;
static const int num_expression_throw_names = 1;
BreakpointResolverSP
ItaniumABILanguageRuntime::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp)
{
return CreateExceptionResolver (bkpt, catch_bp, throw_bp, false);
}
BreakpointResolverSP
ItaniumABILanguageRuntime::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp, bool for_expressions)
{
BreakpointResolverSP resolver_sp;
// One complication here is that most users DON'T want to stop at __cxa_allocate_expression, but until we can do
// anything better with predicting unwinding the expression parser does. So we have two forms of the exception
// breakpoints, one for expressions that leaves out __cxa_allocate_exception, and one that includes it.
// The SetExceptionBreakpoints does the latter, the CreateExceptionBreakpoint in the runtime the former.
uint32_t num_expressions;
if (catch_bp && throw_bp)
{
if (for_expressions)
num_expressions = num_exception_names;
else
num_expressions = num_exception_names - num_expression_throw_names;
resolver_sp.reset (new BreakpointResolverName (bkpt,
exception_names,
num_expressions,
eFunctionNameTypeBase,
eLazyBoolNo));
}
else if (throw_bp)
{
if (for_expressions)
num_expressions = num_throw_names;
else
num_expressions = num_throw_names - num_expression_throw_names;
resolver_sp.reset (new BreakpointResolverName (bkpt,
exception_names + num_catch_names,
num_expressions,
eFunctionNameTypeBase,
eLazyBoolNo));
}
else if (catch_bp)
resolver_sp.reset (new BreakpointResolverName (bkpt,
exception_names,
num_catch_names,
eFunctionNameTypeBase,
eLazyBoolNo));
return resolver_sp;
}
void
ItaniumABILanguageRuntime::SetExceptionBreakpoints ()
{
if (!m_process)
return;
const bool catch_bp = false;
const bool throw_bp = true;
const bool is_internal = true;
const bool for_expressions = true;
// For the exception breakpoints set by the Expression parser, we'll be a little more aggressive and
// stop at exception allocation as well.
if (!m_cxx_exception_bp_sp)
{
Target &target = m_process->GetTarget();
FileSpecList filter_modules;
// Limit the number of modules that are searched for these breakpoints for
// Apple binaries.
if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple)
{
filter_modules.Append(FileSpec("libc++abi.dylib", false));
filter_modules.Append(FileSpec("libSystem.B.dylib", false));
}
BreakpointResolverSP exception_resolver_sp = CreateExceptionResolver (NULL, catch_bp, throw_bp, for_expressions);
SearchFilterSP filter_sp;
if (filter_modules.IsEmpty())
filter_sp = target.GetSearchFilterForModule(NULL);
else
filter_sp = target.GetSearchFilterForModuleList(&filter_modules);
m_cxx_exception_bp_sp = target.CreateBreakpoint (filter_sp, exception_resolver_sp, is_internal);
if (m_cxx_exception_bp_sp)
m_cxx_exception_bp_sp->SetBreakpointKind("c++ exception");
}
else
m_cxx_exception_bp_sp->SetEnabled (true);
}
void
ItaniumABILanguageRuntime::ClearExceptionBreakpoints ()
{
if (!m_process)
return;
if (m_cxx_exception_bp_sp.get())
{
m_cxx_exception_bp_sp->SetEnabled (false);
}
}
bool
ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop (lldb::StopInfoSP stop_reason)
{
if (!m_process)
return false;
if (!stop_reason ||
stop_reason->GetStopReason() != eStopReasonBreakpoint)
return false;
uint64_t break_site_id = stop_reason->GetValue();
return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint(break_site_id,
m_cxx_exception_bp_sp->GetID());
}