forked from OSchip/llvm-project
LLDB now correctly handles virtual inheritance.
Test case added as well. <rdar://problem/16785904> llvm-svn: 213433
This commit is contained in:
parent
cfd17dd2be
commit
759e7441af
|
@ -715,6 +715,10 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
// Find the address of the C++ vtable pointer
|
||||
virtual lldb::addr_t
|
||||
GetCPPVTableAddress(AddressType &address_type);
|
||||
|
||||
virtual lldb::ValueObjectSP
|
||||
Cast (const ClangASTType &clang_ast_type);
|
||||
|
||||
|
|
|
@ -426,7 +426,6 @@ public:
|
|||
|
||||
ClangASTType
|
||||
GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
||||
const char *parent_name,
|
||||
size_t idx,
|
||||
bool transparent_pointers,
|
||||
bool omit_empty_base_classes,
|
||||
|
@ -437,7 +436,8 @@ public:
|
|||
uint32_t &child_bitfield_bit_size,
|
||||
uint32_t &child_bitfield_bit_offset,
|
||||
bool &child_is_base_class,
|
||||
bool &child_is_deref_of_parent) const;
|
||||
bool &child_is_deref_of_parent,
|
||||
ValueObject *valobj) const;
|
||||
|
||||
// Lookup a child given a name. This function will match base class names
|
||||
// and member member names in "clang_type" only, not descendants.
|
||||
|
|
|
@ -793,7 +793,6 @@ ValueObject::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_
|
|||
ExecutionContext exe_ctx (GetExecutionContextRef());
|
||||
|
||||
child_clang_type = GetClangType().GetChildClangTypeAtIndex (&exe_ctx,
|
||||
GetName().GetCString(),
|
||||
idx,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -804,7 +803,8 @@ ValueObject::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
child_is_deref_of_parent);
|
||||
child_is_deref_of_parent,
|
||||
this);
|
||||
if (child_clang_type)
|
||||
{
|
||||
if (synthetic_index)
|
||||
|
@ -3468,6 +3468,38 @@ ValueObject::CreateConstantValue (const ConstString &name)
|
|||
return valobj_sp;
|
||||
}
|
||||
|
||||
lldb::addr_t
|
||||
ValueObject::GetCPPVTableAddress (AddressType &address_type)
|
||||
{
|
||||
ClangASTType pointee_type;
|
||||
ClangASTType this_type(GetClangType());
|
||||
uint32_t type_info = this_type.GetTypeInfo(&pointee_type);
|
||||
if (type_info)
|
||||
{
|
||||
bool ptr_or_ref = false;
|
||||
if (type_info & (ClangASTType::eTypeIsPointer | ClangASTType::eTypeIsReference))
|
||||
{
|
||||
ptr_or_ref = true;
|
||||
type_info = pointee_type.GetTypeInfo();
|
||||
}
|
||||
|
||||
const uint32_t cpp_class = ClangASTType::eTypeIsClass | ClangASTType::eTypeIsCPlusPlus;
|
||||
if ((type_info & cpp_class) == cpp_class)
|
||||
{
|
||||
if (ptr_or_ref)
|
||||
{
|
||||
address_type = GetAddressTypeOfChildren();
|
||||
return GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
|
||||
}
|
||||
else
|
||||
return GetAddressOf (false, &address_type);
|
||||
}
|
||||
}
|
||||
|
||||
address_type = eAddressTypeInvalid;
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
ValueObjectSP
|
||||
ValueObject::Dereference (Error &error)
|
||||
{
|
||||
|
@ -3494,7 +3526,6 @@ ValueObject::Dereference (Error &error)
|
|||
ExecutionContext exe_ctx (GetExecutionContextRef());
|
||||
|
||||
child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx,
|
||||
GetName().GetCString(),
|
||||
0,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -3505,7 +3536,8 @@ ValueObject::Dereference (Error &error)
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
child_is_deref_of_parent);
|
||||
child_is_deref_of_parent,
|
||||
this);
|
||||
if (child_clang_type && child_byte_size)
|
||||
{
|
||||
ConstString child_name;
|
||||
|
|
|
@ -109,7 +109,6 @@ ValueObjectConstResultImpl::CreateChildAtIndex (size_t idx, bool synthetic_array
|
|||
ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef());
|
||||
|
||||
child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx,
|
||||
m_impl_backend->GetName().GetCString(),
|
||||
idx,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -120,7 +119,8 @@ ValueObjectConstResultImpl::CreateChildAtIndex (size_t idx, bool synthetic_array
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
child_is_deref_of_parent);
|
||||
child_is_deref_of_parent,
|
||||
m_impl_backend);
|
||||
if (child_clang_type && child_byte_size)
|
||||
{
|
||||
if (synthetic_index)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/RecordLayout.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/AST/VTableBuilder.h"
|
||||
|
||||
#include "clang/Basic/Builtins.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
|
@ -3068,7 +3069,6 @@ ClangASTType::GetNumPointeeChildren () const
|
|||
|
||||
ClangASTType
|
||||
ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
||||
const char *parent_name,
|
||||
size_t idx,
|
||||
bool transparent_pointers,
|
||||
bool omit_empty_base_classes,
|
||||
|
@ -3079,7 +3079,8 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
uint32_t &child_bitfield_bit_size,
|
||||
uint32_t &child_bitfield_bit_offset,
|
||||
bool &child_is_base_class,
|
||||
bool &child_is_deref_of_parent) const
|
||||
bool &child_is_deref_of_parent,
|
||||
ValueObject *valobj) const
|
||||
{
|
||||
if (!IsValid())
|
||||
return ClangASTType();
|
||||
|
@ -3146,7 +3147,74 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
|
||||
|
||||
if (base_class->isVirtual())
|
||||
bit_offset = record_layout.getVBaseClassOffset(base_class_decl).getQuantity() * 8;
|
||||
{
|
||||
bool handled = false;
|
||||
if (valobj)
|
||||
{
|
||||
Error err;
|
||||
AddressType addr_type = eAddressTypeInvalid;
|
||||
lldb::addr_t vtable_ptr_addr = valobj->GetCPPVTableAddress(addr_type);
|
||||
|
||||
if (vtable_ptr_addr != LLDB_INVALID_ADDRESS && addr_type == eAddressTypeLoad)
|
||||
{
|
||||
|
||||
ExecutionContext exe_ctx (valobj->GetExecutionContextRef());
|
||||
Process *process = exe_ctx.GetProcessPtr();
|
||||
if (process)
|
||||
{
|
||||
clang::VTableContextBase *vtable_ctx = m_ast->getVTableContext();
|
||||
if (vtable_ctx)
|
||||
{
|
||||
if (vtable_ctx->isMicrosoft())
|
||||
{
|
||||
clang::MicrosoftVTableContext *msoft_vtable_ctx = static_cast<clang::MicrosoftVTableContext *>(vtable_ctx);
|
||||
|
||||
if (vtable_ptr_addr)
|
||||
{
|
||||
const lldb::addr_t vbtable_ptr_addr = vtable_ptr_addr + record_layout.getVBPtrOffset().getQuantity();
|
||||
|
||||
const lldb::addr_t vbtable_ptr = process->ReadPointerFromMemory(vbtable_ptr_addr, err);
|
||||
if (vbtable_ptr != LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
// Get the index into the virtual base table. The index is the index in uint32_t from vbtable_ptr
|
||||
const unsigned vbtable_index = msoft_vtable_ctx->getVBTableIndex(cxx_record_decl, base_class_decl);
|
||||
const lldb::addr_t base_offset_addr = vbtable_ptr + vbtable_index * 4;
|
||||
const uint32_t base_offset = process->ReadUnsignedIntegerFromMemory(base_offset_addr, 4, UINT32_MAX, err);
|
||||
if (base_offset != UINT32_MAX)
|
||||
{
|
||||
handled = true;
|
||||
bit_offset = base_offset * 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
clang::ItaniumVTableContext *itanium_vtable_ctx = static_cast<clang::ItaniumVTableContext *>(vtable_ctx);
|
||||
if (vtable_ptr_addr)
|
||||
{
|
||||
const lldb::addr_t vtable_ptr = process->ReadPointerFromMemory(vtable_ptr_addr, err);
|
||||
if (vtable_ptr != LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
clang::CharUnits base_offset_offset = itanium_vtable_ctx->getVirtualBaseOffsetOffset(cxx_record_decl, base_class_decl);
|
||||
const lldb::addr_t base_offset_addr = vtable_ptr + base_offset_offset.getQuantity();
|
||||
const uint32_t base_offset = process->ReadUnsignedIntegerFromMemory(base_offset_addr, 4, UINT32_MAX, err);
|
||||
if (base_offset != UINT32_MAX)
|
||||
{
|
||||
handled = true;
|
||||
bit_offset = base_offset * 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!handled)
|
||||
bit_offset = record_layout.getVBaseClassOffset(base_class_decl).getQuantity() * 8;
|
||||
}
|
||||
else
|
||||
bit_offset = record_layout.getBaseClassOffset(base_class_decl).getQuantity() * 8;
|
||||
|
||||
|
@ -3321,7 +3389,6 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_is_deref_of_parent = false;
|
||||
bool tmp_child_is_deref_of_parent = false;
|
||||
return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
|
||||
parent_name,
|
||||
idx,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -3332,11 +3399,13 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
tmp_child_is_deref_of_parent);
|
||||
tmp_child_is_deref_of_parent,
|
||||
valobj);
|
||||
}
|
||||
else
|
||||
{
|
||||
child_is_deref_of_parent = true;
|
||||
const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
|
||||
if (parent_name)
|
||||
{
|
||||
child_name.assign(1, '*');
|
||||
|
@ -3411,7 +3480,6 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_is_deref_of_parent = false;
|
||||
bool tmp_child_is_deref_of_parent = false;
|
||||
return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
|
||||
parent_name,
|
||||
idx,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -3422,12 +3490,14 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
tmp_child_is_deref_of_parent);
|
||||
tmp_child_is_deref_of_parent,
|
||||
valobj);
|
||||
}
|
||||
else
|
||||
{
|
||||
child_is_deref_of_parent = true;
|
||||
|
||||
const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
|
||||
if (parent_name)
|
||||
{
|
||||
child_name.assign(1, '*');
|
||||
|
@ -3456,7 +3526,6 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_is_deref_of_parent = false;
|
||||
bool tmp_child_is_deref_of_parent = false;
|
||||
return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
|
||||
parent_name,
|
||||
idx,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -3467,10 +3536,12 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
tmp_child_is_deref_of_parent);
|
||||
tmp_child_is_deref_of_parent,
|
||||
valobj);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
|
||||
if (parent_name)
|
||||
{
|
||||
child_name.assign(1, '&');
|
||||
|
@ -3492,7 +3563,6 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
{
|
||||
ClangASTType typedefed_clang_type (m_ast, llvm::cast<clang::TypedefType>(parent_qual_type)->getDecl()->getUnderlyingType());
|
||||
return typedefed_clang_type.GetChildClangTypeAtIndex (exe_ctx,
|
||||
parent_name,
|
||||
idx,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -3503,7 +3573,8 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
child_is_deref_of_parent);
|
||||
child_is_deref_of_parent,
|
||||
valobj);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -3511,7 +3582,6 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
{
|
||||
ClangASTType elaborated_clang_type (m_ast, llvm::cast<clang::ElaboratedType>(parent_qual_type)->getNamedType());
|
||||
return elaborated_clang_type.GetChildClangTypeAtIndex (exe_ctx,
|
||||
parent_name,
|
||||
idx,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -3522,14 +3592,14 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
child_is_deref_of_parent);
|
||||
child_is_deref_of_parent,
|
||||
valobj);
|
||||
}
|
||||
|
||||
case clang::Type::Paren:
|
||||
{
|
||||
ClangASTType paren_clang_type (m_ast, llvm::cast<clang::ParenType>(parent_qual_type)->desugar());
|
||||
return paren_clang_type.GetChildClangTypeAtIndex (exe_ctx,
|
||||
parent_name,
|
||||
idx,
|
||||
transparent_pointers,
|
||||
omit_empty_base_classes,
|
||||
|
@ -3540,7 +3610,8 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
|
|||
child_bitfield_bit_size,
|
||||
child_bitfield_bit_offset,
|
||||
child_is_base_class,
|
||||
child_is_deref_of_parent);
|
||||
child_is_deref_of_parent,
|
||||
valobj);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
LEVEL = ../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -0,0 +1,63 @@
|
|||
"""
|
||||
Tests that bool types work
|
||||
"""
|
||||
import lldb
|
||||
from lldbtest import *
|
||||
import lldbutil
|
||||
|
||||
class CPPTestDiamondInheritance(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dsym_test
|
||||
def test_with_dsym_and_run_command(self):
|
||||
"""Test that virtual base classes work in when SBValue objects are used to explore the variable value"""
|
||||
self.buildDsym()
|
||||
self.diamong_inheritace()
|
||||
|
||||
@dwarf_test
|
||||
def test_with_dwarf_and_run_command(self):
|
||||
"""Test that virtual base classes work in when SBValue objects are used to explore the variable value"""
|
||||
self.buildDwarf()
|
||||
self.diamong_inheritace()
|
||||
|
||||
def setUp(self):
|
||||
TestBase.setUp(self)
|
||||
|
||||
def set_breakpoint(self, line):
|
||||
# Some compilers (for example GCC 4.4.7 and 4.6.1) emit multiple locations for the statement with the ternary
|
||||
# operator in the test program, while others emit only 1.
|
||||
lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=-1, loc_exact=False)
|
||||
|
||||
def diamong_inheritace(self):
|
||||
"""Test that virtual base classes work in when SBValue objects are used to explore the variable value"""
|
||||
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
self.set_breakpoint(line_number('main.cpp', '// breakpoint 1'))
|
||||
self.set_breakpoint(line_number('main.cpp', '// breakpoint 2'))
|
||||
process = target.LaunchSimple (None, None, self.get_process_working_directory())
|
||||
self.assertTrue(process, PROCESS_IS_VALID)
|
||||
thread = process.GetThreadAtIndex(0)
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
j1 = frame.FindVariable("j1")
|
||||
j1_Derived1 = j1.GetChildAtIndex(0)
|
||||
j1_Derived2 = j1.GetChildAtIndex(1)
|
||||
j1_Derived1_VBase = j1_Derived1.GetChildAtIndex(0)
|
||||
j1_Derived2_VBase = j1_Derived2.GetChildAtIndex(0)
|
||||
j1_Derived1_VBase_m_value = j1_Derived1_VBase.GetChildAtIndex(0)
|
||||
j1_Derived2_VBase_m_value = j1_Derived2_VBase.GetChildAtIndex(0)
|
||||
self.assertTrue(j1_Derived1_VBase.GetLoadAddress() == j1_Derived2_VBase.GetLoadAddress(), "ensure virtual base class is the same between Derived1 and Derived2")
|
||||
self.assertTrue(j1_Derived1_VBase_m_value.GetValueAsUnsigned(1) == j1_Derived2_VBase_m_value.GetValueAsUnsigned(2), "ensure m_value in VBase is the same")
|
||||
self.assertTrue(frame.FindVariable("d").GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned(0) == 12345, "ensure Derived2 from j1 is correct");
|
||||
thread.StepOver()
|
||||
self.assertTrue(frame.FindVariable("d").GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned(0) == 12346, "ensure Derived2 from j2 is correct");
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
lldb.SBDebugger.Initialize()
|
||||
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||
unittest2.main()
|
|
@ -0,0 +1,85 @@
|
|||
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include <stdio.h>
|
||||
|
||||
static int g_next_value = 12345;
|
||||
|
||||
class VBase
|
||||
{
|
||||
public:
|
||||
VBase() : m_value(g_next_value++) {}
|
||||
virtual ~VBase() {}
|
||||
void Print()
|
||||
{
|
||||
printf("%p: %s\n%p: m_value = 0x%8.8x\n", this, __PRETTY_FUNCTION__, &m_value, m_value);
|
||||
}
|
||||
int m_value;
|
||||
};
|
||||
|
||||
class Derived1 : public virtual VBase
|
||||
{
|
||||
public:
|
||||
Derived1() {};
|
||||
void Print ()
|
||||
{
|
||||
printf("%p: %s\n", this, __PRETTY_FUNCTION__);
|
||||
VBase::Print();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class Derived2 : public virtual VBase
|
||||
{
|
||||
public:
|
||||
Derived2() {};
|
||||
|
||||
void Print ()
|
||||
{
|
||||
printf("%p: %s\n", this, __PRETTY_FUNCTION__);
|
||||
VBase::Print();
|
||||
}
|
||||
};
|
||||
|
||||
class Joiner1 : public Derived1, public Derived2
|
||||
{
|
||||
public:
|
||||
Joiner1() :
|
||||
m_joiner1(3456),
|
||||
m_joiner2(6789) {}
|
||||
void Print ()
|
||||
{
|
||||
printf("%p: %s \n%p: m_joiner1 = 0x%8.8x\n%p: m_joiner2 = 0x%8.8x\n",
|
||||
this,
|
||||
__PRETTY_FUNCTION__,
|
||||
&m_joiner1,
|
||||
m_joiner1,
|
||||
&m_joiner2,
|
||||
m_joiner2);
|
||||
Derived1::Print();
|
||||
Derived2::Print();
|
||||
}
|
||||
int m_joiner1;
|
||||
int m_joiner2;
|
||||
};
|
||||
|
||||
class Joiner2 : public Derived2
|
||||
{
|
||||
int m_stuff[32];
|
||||
};
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
Joiner1 j1;
|
||||
Joiner2 j2;
|
||||
j1.Print();
|
||||
j2.Print();
|
||||
Derived2 *d = &j1;
|
||||
d = &j2; // breakpoint 1
|
||||
return 0; // breakpoint 2
|
||||
}
|
Loading…
Reference in New Issue