LLDB now correctly handles virtual inheritance.

Test case added as well.

<rdar://problem/16785904>

llvm-svn: 213433
This commit is contained in:
Greg Clayton 2014-07-19 00:12:57 +00:00
parent cfd17dd2be
commit 759e7441af
8 changed files with 283 additions and 23 deletions

View File

@ -715,6 +715,10 @@ public:
{ {
} }
// Find the address of the C++ vtable pointer
virtual lldb::addr_t
GetCPPVTableAddress(AddressType &address_type);
virtual lldb::ValueObjectSP virtual lldb::ValueObjectSP
Cast (const ClangASTType &clang_ast_type); Cast (const ClangASTType &clang_ast_type);

View File

@ -426,7 +426,6 @@ public:
ClangASTType ClangASTType
GetChildClangTypeAtIndex (ExecutionContext *exe_ctx, GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
const char *parent_name,
size_t idx, size_t idx,
bool transparent_pointers, bool transparent_pointers,
bool omit_empty_base_classes, bool omit_empty_base_classes,
@ -437,7 +436,8 @@ public:
uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_size,
uint32_t &child_bitfield_bit_offset, uint32_t &child_bitfield_bit_offset,
bool &child_is_base_class, 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 // Lookup a child given a name. This function will match base class names
// and member member names in "clang_type" only, not descendants. // and member member names in "clang_type" only, not descendants.

View File

@ -793,7 +793,6 @@ ValueObject::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_
ExecutionContext exe_ctx (GetExecutionContextRef()); ExecutionContext exe_ctx (GetExecutionContextRef());
child_clang_type = GetClangType().GetChildClangTypeAtIndex (&exe_ctx, child_clang_type = GetClangType().GetChildClangTypeAtIndex (&exe_ctx,
GetName().GetCString(),
idx, idx,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, 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_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, child_is_base_class,
child_is_deref_of_parent); child_is_deref_of_parent,
this);
if (child_clang_type) if (child_clang_type)
{ {
if (synthetic_index) if (synthetic_index)
@ -3468,6 +3468,38 @@ ValueObject::CreateConstantValue (const ConstString &name)
return valobj_sp; 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 ValueObjectSP
ValueObject::Dereference (Error &error) ValueObject::Dereference (Error &error)
{ {
@ -3494,7 +3526,6 @@ ValueObject::Dereference (Error &error)
ExecutionContext exe_ctx (GetExecutionContextRef()); ExecutionContext exe_ctx (GetExecutionContextRef());
child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx,
GetName().GetCString(),
0, 0,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, omit_empty_base_classes,
@ -3505,7 +3536,8 @@ ValueObject::Dereference (Error &error)
child_bitfield_bit_size, child_bitfield_bit_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, child_is_base_class,
child_is_deref_of_parent); child_is_deref_of_parent,
this);
if (child_clang_type && child_byte_size) if (child_clang_type && child_byte_size)
{ {
ConstString child_name; ConstString child_name;

View File

@ -109,7 +109,6 @@ ValueObjectConstResultImpl::CreateChildAtIndex (size_t idx, bool synthetic_array
ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef());
child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx,
m_impl_backend->GetName().GetCString(),
idx, idx,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, omit_empty_base_classes,
@ -120,7 +119,8 @@ ValueObjectConstResultImpl::CreateChildAtIndex (size_t idx, bool synthetic_array
child_bitfield_bit_size, child_bitfield_bit_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, 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 (child_clang_type && child_byte_size)
{ {
if (synthetic_index) if (synthetic_index)

View File

@ -22,6 +22,7 @@
#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclTemplate.h"
#include "clang/AST/RecordLayout.h" #include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h" #include "clang/AST/Type.h"
#include "clang/AST/VTableBuilder.h"
#include "clang/Basic/Builtins.h" #include "clang/Basic/Builtins.h"
#include "clang/Basic/IdentifierTable.h" #include "clang/Basic/IdentifierTable.h"
@ -3068,7 +3069,6 @@ ClangASTType::GetNumPointeeChildren () const
ClangASTType ClangASTType
ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx, ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
const char *parent_name,
size_t idx, size_t idx,
bool transparent_pointers, bool transparent_pointers,
bool omit_empty_base_classes, 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_size,
uint32_t &child_bitfield_bit_offset, uint32_t &child_bitfield_bit_offset,
bool &child_is_base_class, bool &child_is_base_class,
bool &child_is_deref_of_parent) const bool &child_is_deref_of_parent,
ValueObject *valobj) const
{ {
if (!IsValid()) if (!IsValid())
return ClangASTType(); return ClangASTType();
@ -3146,7 +3147,74 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
if (base_class->isVirtual()) 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 else
bit_offset = record_layout.getBaseClassOffset(base_class_decl).getQuantity() * 8; 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; child_is_deref_of_parent = false;
bool tmp_child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false;
return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx, return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
parent_name,
idx, idx,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, omit_empty_base_classes,
@ -3332,11 +3399,13 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
child_bitfield_bit_size, child_bitfield_bit_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, child_is_base_class,
tmp_child_is_deref_of_parent); tmp_child_is_deref_of_parent,
valobj);
} }
else else
{ {
child_is_deref_of_parent = true; child_is_deref_of_parent = true;
const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
if (parent_name) if (parent_name)
{ {
child_name.assign(1, '*'); child_name.assign(1, '*');
@ -3411,7 +3480,6 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
child_is_deref_of_parent = false; child_is_deref_of_parent = false;
bool tmp_child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false;
return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx, return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
parent_name,
idx, idx,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, omit_empty_base_classes,
@ -3422,12 +3490,14 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
child_bitfield_bit_size, child_bitfield_bit_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, child_is_base_class,
tmp_child_is_deref_of_parent); tmp_child_is_deref_of_parent,
valobj);
} }
else else
{ {
child_is_deref_of_parent = true; child_is_deref_of_parent = true;
const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
if (parent_name) if (parent_name)
{ {
child_name.assign(1, '*'); child_name.assign(1, '*');
@ -3456,7 +3526,6 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
child_is_deref_of_parent = false; child_is_deref_of_parent = false;
bool tmp_child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false;
return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx, return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
parent_name,
idx, idx,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, omit_empty_base_classes,
@ -3467,10 +3536,12 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
child_bitfield_bit_size, child_bitfield_bit_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, child_is_base_class,
tmp_child_is_deref_of_parent); tmp_child_is_deref_of_parent,
valobj);
} }
else else
{ {
const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
if (parent_name) if (parent_name)
{ {
child_name.assign(1, '&'); 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()); ClangASTType typedefed_clang_type (m_ast, llvm::cast<clang::TypedefType>(parent_qual_type)->getDecl()->getUnderlyingType());
return typedefed_clang_type.GetChildClangTypeAtIndex (exe_ctx, return typedefed_clang_type.GetChildClangTypeAtIndex (exe_ctx,
parent_name,
idx, idx,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, omit_empty_base_classes,
@ -3503,7 +3573,8 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
child_bitfield_bit_size, child_bitfield_bit_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, child_is_base_class,
child_is_deref_of_parent); child_is_deref_of_parent,
valobj);
} }
break; break;
@ -3511,7 +3582,6 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
{ {
ClangASTType elaborated_clang_type (m_ast, llvm::cast<clang::ElaboratedType>(parent_qual_type)->getNamedType()); ClangASTType elaborated_clang_type (m_ast, llvm::cast<clang::ElaboratedType>(parent_qual_type)->getNamedType());
return elaborated_clang_type.GetChildClangTypeAtIndex (exe_ctx, return elaborated_clang_type.GetChildClangTypeAtIndex (exe_ctx,
parent_name,
idx, idx,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, omit_empty_base_classes,
@ -3522,14 +3592,14 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
child_bitfield_bit_size, child_bitfield_bit_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, child_is_base_class,
child_is_deref_of_parent); child_is_deref_of_parent,
valobj);
} }
case clang::Type::Paren: case clang::Type::Paren:
{ {
ClangASTType paren_clang_type (m_ast, llvm::cast<clang::ParenType>(parent_qual_type)->desugar()); ClangASTType paren_clang_type (m_ast, llvm::cast<clang::ParenType>(parent_qual_type)->desugar());
return paren_clang_type.GetChildClangTypeAtIndex (exe_ctx, return paren_clang_type.GetChildClangTypeAtIndex (exe_ctx,
parent_name,
idx, idx,
transparent_pointers, transparent_pointers,
omit_empty_base_classes, omit_empty_base_classes,
@ -3540,7 +3610,8 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
child_bitfield_bit_size, child_bitfield_bit_size,
child_bitfield_bit_offset, child_bitfield_bit_offset,
child_is_base_class, child_is_base_class,
child_is_deref_of_parent); child_is_deref_of_parent,
valobj);
} }

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

@ -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()

View File

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