xref: /llvm-project/lldb/test/API/functionalities/vtable/TestVTableValue.py (revision abb6919a63c7ef017bb4f9c86057adcdb8129964)
1"""
2Make sure the getting a variable path works and doesn't crash.
3"""
4
5import lldb
6import lldbsuite.test.lldbutil as lldbutil
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9
10
11class TestVTableValue(TestBase):
12    # If your test case doesn't stress debug info, then
13    # set this to true.  That way it won't be run once for
14    # each debug info format.
15    NO_DEBUG_INFO_TESTCASE = True
16
17    @skipIf(compiler="clang", compiler_version=["<", "9.0"])
18    @skipUnlessPlatform(["linux", "macosx"])
19    def test_vtable(self):
20        self.build()
21        lldbutil.run_to_source_breakpoint(
22            self, "At the end", lldb.SBFileSpec("main.cpp")
23        )
24
25        # Test a shape instance to make sure we get the vtable correctly.
26        shape = self.frame().FindVariable("shape")
27        vtable = shape.GetVTable()
28        self.assertEqual(vtable.GetName(), "vtable for Shape")
29        self.assertEqual(vtable.GetTypeName(), "vtable for Shape")
30        # Make sure we have the right number of virtual functions in our vtable
31        # for the shape class.
32        self.assertEqual(vtable.GetNumChildren(), 4)
33
34        # Verify vtable address
35        vtable_addr = vtable.GetValueAsUnsigned(0)
36        expected_addr = self.expected_vtable_addr(shape)
37        self.assertEqual(vtable_addr, expected_addr)
38
39        for idx, vtable_entry in enumerate(vtable.children):
40            self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
41
42        # Test a shape reference to make sure we get the vtable correctly.
43        shape = self.frame().FindVariable("shape_ref")
44        vtable = shape.GetVTable()
45        self.assertEqual(vtable.GetName(), "vtable for Shape")
46        self.assertEqual(vtable.GetTypeName(), "vtable for Shape")
47        # Make sure we have the right number of virtual functions in our vtable
48        # for the shape class.
49        self.assertEqual(vtable.GetNumChildren(), 4)
50
51        # Verify vtable address
52        vtable_addr = vtable.GetValueAsUnsigned(0)
53        expected_addr = self.expected_vtable_addr(shape)
54        self.assertEqual(vtable_addr, expected_addr)
55
56        for idx, vtable_entry in enumerate(vtable.children):
57            self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
58
59        # Test we get the right vtable for the Rectangle instance.
60        rect = self.frame().FindVariable("rect")
61        vtable = rect.GetVTable()
62        self.assertEqual(vtable.GetName(), "vtable for Rectangle")
63        self.assertEqual(vtable.GetTypeName(), "vtable for Rectangle")
64
65        # Make sure we have the right number of virtual functions in our vtable
66        # with the extra virtual function added by the Rectangle class
67        self.assertEqual(vtable.GetNumChildren(), 5)
68
69        # Verify vtable address
70        vtable_addr = vtable.GetValueAsUnsigned()
71        expected_addr = self.expected_vtable_addr(rect)
72        self.assertEqual(vtable_addr, expected_addr)
73
74        for idx, vtable_entry in enumerate(vtable.children):
75            self.verify_vtable_entry(vtable_entry, vtable_addr, idx)
76
77    @skipIf(compiler="clang", compiler_version=["<", "9.0"])
78    @skipUnlessPlatform(["linux", "macosx"])
79    def test_base_class_ptr(self):
80        self.build()
81        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
82            self, "Shape is Rectangle", lldb.SBFileSpec("main.cpp")
83        )
84
85        shape = self.frame().FindVariable("shape")
86        rect = self.frame().FindVariable("rect")
87
88        shape_ptr = self.frame().FindVariable("shape_ptr")
89        shape_ptr_vtable = shape_ptr.GetVTable()
90        self.assertEqual(shape_ptr_vtable.GetName(), "vtable for Rectangle")
91        self.assertEqual(shape_ptr_vtable.GetNumChildren(), 5)
92        self.assertEqual(shape_ptr.GetValueAsUnsigned(0), rect.GetLoadAddress())
93        lldbutil.continue_to_source_breakpoint(
94            self, process, "Shape is Shape", lldb.SBFileSpec("main.cpp")
95        )
96        self.assertEqual(shape_ptr.GetValueAsUnsigned(0), shape.GetLoadAddress())
97        self.assertEqual(shape_ptr_vtable.GetNumChildren(), 4)
98        self.assertEqual(shape_ptr_vtable.GetName(), "vtable for Shape")
99
100    @skipUnlessPlatform(["linux", "macosx"])
101    def test_no_vtable(self):
102        self.build()
103        lldbutil.run_to_source_breakpoint(
104            self, "At the end", lldb.SBFileSpec("main.cpp")
105        )
106
107        var = self.frame().FindVariable("not_virtual")
108        self.assertEqual(
109            var.GetVTable().GetError().GetCString(),
110            'type "NotVirtual" doesn\'t have a vtable',
111        )
112
113        var = self.frame().FindVariable("argc")
114        self.assertEqual(
115            var.GetVTable().GetError().GetCString(),
116            'no language runtime support for the language "c"',
117        )
118
119    @skipUnlessPlatform(["linux", "macosx"])
120    def test_overwrite_vtable(self):
121        self.build()
122        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
123            self, "At the end", lldb.SBFileSpec("main.cpp")
124        )
125
126        # Test a shape instance to make sure we get the vtable correctly.
127        shape = self.frame().FindVariable("shape")
128        vtable = shape.GetVTable()
129        self.assertEqual(vtable.GetName(), "vtable for Shape")
130        self.assertEqual(vtable.GetTypeName(), "vtable for Shape")
131        # Make sure we have the right number of virtual functions in our vtable
132        # for the shape class.
133        self.assertEqual(vtable.GetNumChildren(), 4)
134
135        # Overwrite the first entry in the vtable and make sure we can still
136        # see the bogus value which should have no summary
137        vtable_addr = vtable.GetValueAsUnsigned()
138
139        is_64bit = self.process().GetAddressByteSize() == 8
140        data = str(
141            "\x01\x01\x01\x01\x01\x01\x01\x01" if is_64bit else "\x01\x01\x01\x01"
142        )
143        error = lldb.SBError()
144        bytes_written = process.WriteMemory(vtable_addr, data, error)
145
146        self.assertSuccess(error)
147        self.assertGreater(
148            bytes_written, 0, "Failed to overwrite first entry in vtable"
149        )
150
151        scribbled_child = vtable.GetChildAtIndex(0)
152        self.assertEqual(
153            scribbled_child.GetValueAsUnsigned(0),
154            0x0101010101010101 if is_64bit else 0x01010101,
155        )
156        self.assertEqual(scribbled_child.GetSummary(), None)
157
158    def expected_vtable_addr(self, var: lldb.SBValue) -> int:
159        load_addr = var.GetLoadAddress()
160        read_from_memory_error = lldb.SBError()
161        vtable_addr = self.process().ReadPointerFromMemory(
162            load_addr, read_from_memory_error
163        )
164        self.assertTrue(read_from_memory_error.Success())
165        return vtable_addr
166
167    def expected_vtable_entry_func_ptr(self, vtable_addr: int, idx: int):
168        vtable_entry_addr = vtable_addr + idx * self.process().GetAddressByteSize()
169        read_func_ptr_error = lldb.SBError()
170        func_ptr = self.process().ReadPointerFromMemory(
171            vtable_entry_addr, read_func_ptr_error
172        )
173        self.assertTrue(read_func_ptr_error.Success())
174        return func_ptr
175
176    def verify_vtable_entry(
177        self, vtable_entry: lldb.SBValue, vtable_addr: int, idx: int
178    ):
179        """Verify the vtable entry looks something like:
180
181        (double ()) [0] = 0x0000000100003a10 a.out`Rectangle::Area() at main.cpp:14
182
183        """
184        # Check function ptr
185        vtable_entry_func_ptr = vtable_entry.GetValueAsUnsigned(0)
186        self.assertEqual(
187            vtable_entry_func_ptr,
188            self.expected_vtable_entry_func_ptr(vtable_addr, idx),
189        )
190
191        sb_addr = self.target().ResolveLoadAddress(vtable_entry_func_ptr)
192        sym_ctx = sb_addr.GetSymbolContext(lldb.eSymbolContextEverything)
193
194        # Make sure the type is the same as the function type
195        func_type = sym_ctx.GetFunction().GetType()
196        if func_type.IsValid():
197            self.assertEqual(vtable_entry.GetType(), func_type.GetPointerType())
198
199        # The summary should be the address description of the function pointer
200        summary = vtable_entry.GetSummary()
201        self.assertEqual(str(sb_addr), summary)
202