From 759e7441af95cbd11c65a5f21de00a99cd2a2bed Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Sat, 19 Jul 2014 00:12:57 +0000 Subject: [PATCH] LLDB now correctly handles virtual inheritance. Test case added as well. llvm-svn: 213433 --- lldb/include/lldb/Core/ValueObject.h | 4 + lldb/include/lldb/Symbol/ClangASTType.h | 4 +- lldb/source/Core/ValueObject.cpp | 40 +++++++++- lldb/source/Core/ValueObjectConstResultImpl.cpp | 4 +- lldb/source/Symbol/ClangASTType.cpp | 101 ++++++++++++++++++++---- lldb/test/lang/cpp/diamond/Makefile | 5 ++ lldb/test/lang/cpp/diamond/TestDiamond.py | 63 +++++++++++++++ lldb/test/lang/cpp/diamond/main.cpp | 85 ++++++++++++++++++++ 8 files changed, 283 insertions(+), 23 deletions(-) create mode 100644 lldb/test/lang/cpp/diamond/Makefile create mode 100644 lldb/test/lang/cpp/diamond/TestDiamond.py create mode 100644 lldb/test/lang/cpp/diamond/main.cpp diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index ccda33a..3d133ef 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -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); diff --git a/lldb/include/lldb/Symbol/ClangASTType.h b/lldb/include/lldb/Symbol/ClangASTType.h index 1779f45..4dd1703 100644 --- a/lldb/include/lldb/Symbol/ClangASTType.h +++ b/lldb/include/lldb/Symbol/ClangASTType.h @@ -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. diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 9fc4167..50b917f 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -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; diff --git a/lldb/source/Core/ValueObjectConstResultImpl.cpp b/lldb/source/Core/ValueObjectConstResultImpl.cpp index 8c3e4a1..d3e2758 100644 --- a/lldb/source/Core/ValueObjectConstResultImpl.cpp +++ b/lldb/source/Core/ValueObjectConstResultImpl.cpp @@ -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) diff --git a/lldb/source/Symbol/ClangASTType.cpp b/lldb/source/Symbol/ClangASTType.cpp index ffccf36..f11daaf 100644 --- a/lldb/source/Symbol/ClangASTType.cpp +++ b/lldb/source/Symbol/ClangASTType.cpp @@ -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(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(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(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(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(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); } diff --git a/lldb/test/lang/cpp/diamond/Makefile b/lldb/test/lang/cpp/diamond/Makefile new file mode 100644 index 0000000..314f1cb --- /dev/null +++ b/lldb/test/lang/cpp/diamond/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/lang/cpp/diamond/TestDiamond.py b/lldb/test/lang/cpp/diamond/TestDiamond.py new file mode 100644 index 0000000..247da48 --- /dev/null +++ b/lldb/test/lang/cpp/diamond/TestDiamond.py @@ -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() diff --git a/lldb/test/lang/cpp/diamond/main.cpp b/lldb/test/lang/cpp/diamond/main.cpp new file mode 100644 index 0000000..bfe098a --- /dev/null +++ b/lldb/test/lang/cpp/diamond/main.cpp @@ -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 + +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 +} -- 2.7.4