blob: 7f044ee4e78f69ae8c4f8c96b18aa29e427378b8 [file] [edit]
"""
Test that we can resolve dynamic values in complex inheritance hierarchies.
This is mainly relevant for the Microsoft ABI where there can be multiple vtables in one type.
"""
import lldb
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
class DynamicValueComplexTestCase(TestBase):
SHARED_BUILD_TESTCASE = False
def test_rtti(self):
"""Test with RTTI"""
self.build(dictionary={"CXXFLAGS_EXTRAS": ""})
self.do_test(True)
def test_no_rtti(self):
"""Test without RTTI"""
self.build(dictionary={"CXXFLAGS_EXTRAS": "-fno-rtti"})
self.do_test(False)
def do_test(self, has_rtti: bool):
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
self, "// break here", lldb.SBFileSpec("main.cpp")
)
frame = thread.GetFrameAtIndex(0)
get = lambda name: self._check_get(frame, name)
is_windows_msvc = self.getPlatform() == "windows" and "-msvc" in (
frame.GetModule().GetTriple() or ""
)
# Test 'Single'.
# struct Single : Base1
single = get("single")
single_base1 = get("single_base1")
single_addr = single.GetValueAsAddress()
self.assertNotEqual(single_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertEqual(single_addr, single_base1.GetValueAsAddress())
self.assertEqual(single.GetTypeName(), single_base1.GetTypeName())
single_children = [
ValueCheck(
type="Base1",
children=[ValueCheck(type="int", name="base1", value="1")],
),
ValueCheck(type="int", name="single", value="3"),
]
self.expect_expr(
"single", result_type="Single *", result_children=single_children
)
# Test 'Multiple'.
# struct Multiple : virtual VBase, Base1, Base2
multi = get("multi")
multi_base1 = get("multi_base1")
multi_base2 = get("multi_base2")
multi_addr = multi.GetValueAsAddress()
self.assertNotEqual(multi_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertEqual(multi_addr, multi_base1.GetValueAsAddress())
self.assertEqual(multi_addr, multi_base2.GetValueAsAddress())
self.assertEqual(multi.GetTypeName(), multi_base1.GetTypeName())
self.assertEqual(multi.GetTypeName(), multi_base2.GetTypeName())
multi_children = [
ValueCheck(
type="VBase",
children=[ValueCheck(type="int", name="vbase", value="5")],
),
ValueCheck(
type="Base1",
children=[ValueCheck(type="int", name="base1", value="1")],
),
ValueCheck(
type="Base2",
children=[ValueCheck(type="int", name="base2", value="2")],
),
ValueCheck(type="int", name="multiple", value="4"),
]
# The index of 'VBase' in 'Multiple' depends on the ABI.
# On Itanium, it sits at the start whereas on Microsoft it's after
# the non-virtual ones.
multiple_type = self.target().FindFirstType("Multiple")
self.assertTrue(multiple_type.IsValid())
first_base = multiple_type.GetDirectBaseClassAtIndex(0).GetName()
vbases_at_start = first_base == "VBase"
if not vbases_at_start:
# Move 'VBase' after 'Base2'.
self.assertEqual(first_base, "Base1")
multi_children.insert(2, multi_children.pop(0))
self.expect_expr(
"multi", result_type="Multiple *", result_children=multi_children
)
# Test 'VBaseDiamondUser'.
# struct VBaseDiamondUser : VBaseUser1, VBaseUser2
vbuser = get("vbuser")
vbuser_vbase = get("vbuser_vbase")
vbuser_addr = vbuser.GetValueAsAddress()
if is_windows_msvc and not has_rtti:
# On Windows, we have to use the AST to get the offset.
# But we don't know where the virtual base will be located in the object.
self.assertNotEqual(vbuser_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertNotEqual(
vbuser_vbase.GetValueAsAddress(), lldb.LLDB_INVALID_ADDRESS
)
self.assertNotEqual(vbuser_addr, vbuser_vbase.GetValueAsAddress())
self.assertNotEqual(vbuser.GetTypeName(), vbuser_vbase.GetTypeName())
else:
self.assertNotEqual(vbuser_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertEqual(vbuser_addr, vbuser_vbase.GetValueAsAddress())
self.assertEqual(vbuser.GetTypeName(), vbuser_vbase.GetTypeName())
vbuser_children = [
ValueCheck(
type="VBaseUser1",
children=[
ValueCheck(
type="VBase",
children=[ValueCheck(type="int", name="vbase", value="5")],
),
ValueCheck(type="int", name="vbase_user1", value="6"),
],
),
ValueCheck(
type="VBaseUser2",
children=[
ValueCheck(
type="VBase",
children=[ValueCheck(type="int", name="vbase", value="5")],
),
ValueCheck(type="int", name="vbase_user2", value="7"),
],
),
ValueCheck(type="int", name="vbase_duo_user", value="8"),
]
self.expect_expr(
"vbuser", result_type="VBaseDiamondUser *", result_children=vbuser_children
)
# Test 'Combined'.
# struct Combined : MultiWrap1, VBaseDiamondUser, MultiWrap2, Single
combined = get("combined")
combined_addr = combined.GetValueAsAddress()
combined_name = combined.GetTypeName() or ""
self.assertNotEqual(combined_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertGreater(len(combined_name), 0)
combined_mw1 = get("combined_mw1")
self.assertEqual(combined_addr, combined_mw1.GetValueAsAddress())
self.assertEqual(combined_name, combined_mw1.GetTypeName())
combined_mw1_multi = get("combined_mw1_multi")
self.assertEqual(combined_addr, combined_mw1_multi.GetValueAsAddress())
self.assertEqual(combined_name, combined_mw1_multi.GetTypeName())
combined_mw1_multi_b1 = get("combined_mw1_multi_b1")
self.assertEqual(combined_addr, combined_mw1_multi_b1.GetValueAsAddress())
self.assertEqual(combined_name, combined_mw1_multi_b1.GetTypeName())
combined_mw1_multi_b2 = get("combined_mw1_multi_b2")
self.assertEqual(combined_addr, combined_mw1_multi_b2.GetValueAsAddress())
self.assertEqual(combined_name, combined_mw1_multi_b2.GetTypeName())
combined_vbuser = get("combined_vbuser")
self.assertEqual(combined_addr, combined_vbuser.GetValueAsAddress())
self.assertEqual(combined_name, combined_vbuser.GetTypeName())
combined_vbuser_vbase = get("combined_vbuser_vbase")
if is_windows_msvc and not has_rtti:
vbuser_vbase_addr = combined_vbuser_vbase.GetValueAsAddress()
self.assertNotEqual(vbuser_vbase_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertNotEqual(combined_addr, vbuser_vbase_addr)
self.assertNotEqual(combined_name, combined_vbuser_vbase.GetTypeName())
else:
self.assertEqual(combined_addr, combined_vbuser_vbase.GetValueAsAddress())
self.assertEqual(combined_name, combined_vbuser_vbase.GetTypeName())
combined_mw2 = get("combined_mw2")
self.assertEqual(combined_addr, combined_mw2.GetValueAsAddress())
self.assertEqual(combined_name, combined_mw2.GetTypeName())
combined_mw2_multi = get("combined_mw2_multi")
self.assertEqual(combined_addr, combined_mw2_multi.GetValueAsAddress())
self.assertEqual(combined_name, combined_mw2_multi.GetTypeName())
combined_mw2_multi_b1 = get("combined_mw2_multi_b1")
self.assertEqual(combined_addr, combined_mw2_multi_b1.GetValueAsAddress())
self.assertEqual(combined_name, combined_mw2_multi_b1.GetTypeName())
combined_mw2_multi_b2 = get("combined_mw2_multi_b2")
self.assertEqual(combined_addr, combined_mw2_multi_b2.GetValueAsAddress())
self.assertEqual(combined_name, combined_mw2_multi_b2.GetTypeName())
combined_single = get("combined_single")
self.assertEqual(combined_addr, combined_single.GetValueAsAddress())
self.assertEqual(combined_name, combined_single.GetTypeName())
combined_single_base1 = get("combined_single_base1")
self.assertEqual(combined_addr, combined_single_base1.GetValueAsAddress())
self.assertEqual(combined_name, combined_single_base1.GetTypeName())
combined_children = [
ValueCheck(
type="MultiWrap1",
children=[
ValueCheck(type="Multiple", children=multi_children),
ValueCheck(type="int", name="multi_wrap1", value="9"),
],
),
ValueCheck(type="VBaseDiamondUser", children=vbuser_children),
ValueCheck(
type="MultiWrap2",
children=[
ValueCheck(type="Multiple", children=multi_children),
ValueCheck(type="int", name="multi_wrap2", value="10"),
],
),
ValueCheck(type="Single", children=single_children),
ValueCheck(type="int", name="combined", value="11"),
]
self.expect_expr(
"combined", result_type="Combined *", result_children=combined_children
)
# Test 'TwoBase1s'
# struct TwoBase1s : virtual VBaseWithBase, Single
tb1 = get("tb1")
tb1_addr = tb1.GetValueAsAddress()
tb1_name = tb1.GetTypeName() or ""
self.assertNotEqual(tb1_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertGreater(len(tb1_name), 0)
tb1_vb = get("tb1_vb")
tb1_vb_b1 = get("tb1_vb_b1")
if is_windows_msvc and not has_rtti:
# Anything that goes through the virtual base is unknown to us, so
# it should not have a dynamic type.
tb1_vb_addr = tb1_vb.GetValueAsAddress()
tb1_vb_b1_addr = tb1_vb_b1.GetValueAsAddress()
self.assertNotEqual(tb1_vb_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertNotEqual(tb1_vb_b1_addr, lldb.LLDB_INVALID_ADDRESS)
self.assertNotEqual(tb1_addr, tb1_vb_addr)
self.assertNotEqual(tb1_addr, tb1_vb_b1_addr)
self.assertNotEqual(tb1_name, tb1_vb.GetTypeName())
self.assertNotEqual(tb1_name, tb1_vb_b1.GetTypeName())
else:
self.assertEqual(tb1_addr, tb1_vb.GetValueAsAddress())
self.assertEqual(tb1_name, tb1_vb.GetTypeName())
self.assertEqual(tb1_addr, tb1_vb_b1.GetValueAsAddress())
self.assertEqual(tb1_name, tb1_vb_b1.GetTypeName())
tb1_single = get("tb1_single")
self.assertEqual(tb1_addr, tb1_single.GetValueAsAddress())
self.assertEqual(tb1_name, tb1_single.GetTypeName())
tb1_single_b1 = get("tb1_single_b1")
self.assertEqual(tb1_addr, tb1_single_b1.GetValueAsAddress())
self.assertEqual(tb1_name, tb1_single_b1.GetTypeName())
tb1_children = [
ValueCheck(
type="VBaseWithBase",
children=[
ValueCheck(
type="Base1",
children=[ValueCheck(type="int", name="base1", value="1")],
),
ValueCheck(type="int", name="vbase_with_base", value="12"),
],
),
ValueCheck(type="Single", children=single_children),
ValueCheck(type="int", name="two_base1s", value="13"),
]
if not vbases_at_start:
tb1_children.insert(1, tb1_children.pop(0))
self.expect_expr("tb1", result_type="TwoBase1s *", result_children=tb1_children)
def _check_get(self, frame: lldb.SBFrame, name: str) -> lldb.SBValue:
var = frame.FindVariable(name, lldb.eDynamicCanRunTarget)
self.assertTrue(var, f"could not find '{name}'")
return var