dump-class-layout reports wrong padding in many cases
[WebKit-https.git] / Tools / lldb / dump_class_layout_unittest.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # Copyright (C) 2018 Apple Inc. All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1.  Redistributions of source code must retain the above copyright
10 #     notice, this list of conditions and the following disclaimer.
11 # 2.  Redistributions in binary form must reproduce the above copyright
12 #     notice, this list of conditions and the following disclaimer in the
13 #     documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
16 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
19 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26 import atexit
27 import difflib
28 import lldb
29 import os
30 import sys
31 import unittest
32
33 from webkitpy.common.system.systemhost import SystemHost
34 from webkitpy.port.config import Config
35
36 from lldb_dump_class_layout import LLDBDebuggerInstance, ClassLayoutBase
37
38 _host = SystemHost()
39
40 # Run these tests with ./Tools/Scripts/test-webkitpy dump_class_layout_unittest
41 # Run a single test with e.g. ./Tools/Scripts/test-webkitpy dump_class_layout_unittest.TestDumpClassLayout.serial_test_ClassWithUniquePtrs
42 # Compare with clang's output: clang++ -Xclang -fdump-record-layouts DumpClassLayoutTesting.cpp
43
44 debugger_instance = None
45
46
47 @atexit.register
48 def destroy_cached_debug_session():
49     debugger_instance = None
50
51
52 class TestDumpClassLayout(unittest.TestCase):
53     @classmethod
54     def shouldSkip(cls):
55         return not _host.platform.is_mac()
56
57     @classmethod
58     def setUpClass(cls):
59         global debugger_instance
60         if not debugger_instance:
61             LLDB_WEBKIT_TESTER_NAME = 'lldbWebKitTester'
62
63             config = Config(_host.executive, _host.filesystem)
64             lldbWebKitTesterExecutable = os.path.join(config.build_directory(config.default_configuration()), LLDB_WEBKIT_TESTER_NAME)
65
66             architecture = 'x86_64'
67             debugger_instance = LLDBDebuggerInstance(lldbWebKitTesterExecutable, architecture)
68             if not debugger_instance:
69                 print 'Failed to create lldb debugger instance for %s' % (lldbWebKitTesterExecutable)
70
71     def setUp(self):
72         super(TestDumpClassLayout, self).setUp()
73         self.maxDiff = None
74         self.addTypeEqualityFunc(str, self.assertMultiLineEqual)
75
76     def serial_test_BasicClassLayout(self):
77         EXPECTED_RESULT = """  +0 <  8> BasicClassLayout
78   +0 <  4>   int intMember
79   +4 <  1>   bool boolMember
80   +5 <  3>   <PADDING: 3 bytes>
81 Total byte size: 8
82 Total pad bytes: 3
83 Padding percentage: 37.50 %"""
84         actual_layout = debugger_instance.layout_for_classname('BasicClassLayout')
85         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
86
87     def serial_test_PaddingBetweenClassMembers(self):
88         EXPECTED_RESULT = """  +0 < 16> PaddingBetweenClassMembers
89   +0 <  8>     BasicClassLayout basic1
90   +0 <  4>       int intMember
91   +4 <  1>       bool boolMember
92   +5 <  3>   <PADDING: 3 bytes>
93   +8 <  8>     BasicClassLayout basic2
94   +8 <  4>       int intMember
95  +12 <  1>       bool boolMember
96  +13 <  3>   <PADDING: 3 bytes>
97 Total byte size: 16
98 Total pad bytes: 6
99 Padding percentage: 37.50 %"""
100         actual_layout = debugger_instance.layout_for_classname('PaddingBetweenClassMembers')
101         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
102
103     def serial_test_BoolPaddingClass(self):
104         EXPECTED_RESULT = """  +0 < 12> BoolPaddingClass
105   +0 <  1>   bool bool1
106   +1 <  1>   bool bool2
107   +2 <  1>   bool bool3
108   +3 <  1>   <PADDING: 1 byte>
109   +4 <  8>     BoolMemberFirst memberClass
110   +4 <  1>       bool boolMember
111   +9 <  3>       <PADDING: 3 bytes>
112   +8 <  4>       int intMember
113 Total byte size: 12
114 Total pad bytes: 4
115 Padding percentage: 33.33 %"""
116         actual_layout = debugger_instance.layout_for_classname('BoolPaddingClass')
117         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
118
119     def serial_test_ClassWithEmptyClassMembers(self):
120         EXPECTED_RESULT = """  +0 < 12> ClassWithEmptyClassMembers
121   +0 <  4>   int intMember
122   +4 <  1>     EmptyClass empty1
123   +4 <  1>   <PADDING: 1 byte>
124   +5 <  1>   bool boolMember
125   +6 <  1>     EmptyClass empty2
126   +6 <  2>   <PADDING: 2 bytes>
127   +8 <  4>   int intMember2
128 Total byte size: 12
129 Total pad bytes: 3
130 Padding percentage: 25.00 %"""
131         actual_layout = debugger_instance.layout_for_classname('ClassWithEmptyClassMembers')
132         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
133
134     def serial_test_SimpleVirtualClass(self):
135         EXPECTED_RESULT = """  +0 < 24> SimpleVirtualClass
136   +0 <  8>    __vtbl_ptr_type * _vptr
137   +8 <  4>   int intMember
138  +12 <  4>   <PADDING: 4 bytes>
139  +16 <  8>   double doubleMember
140 Total byte size: 24
141 Total pad bytes: 4
142 Padding percentage: 16.67 %"""
143         actual_layout = debugger_instance.layout_for_classname('SimpleVirtualClass')
144         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
145
146     def serial_test_VirtualClassWithNonVirtualBase(self):
147         EXPECTED_RESULT = """  +0 < 24> VirtualClassWithNonVirtualBase
148   +0 <  8>    __vtbl_ptr_type * _vptr
149   +8 <  8>     BasicClassLayout BasicClassLayout
150   +8 <  4>       int intMember
151  +12 <  1>       bool boolMember
152  +13 <  3>   <PADDING: 3 bytes>
153  +16 <  8>   double doubleMember
154 Total byte size: 24
155 Total pad bytes: 3
156 Padding percentage: 12.50 %"""
157         actual_layout = debugger_instance.layout_for_classname('VirtualClassWithNonVirtualBase')
158         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
159
160     def serial_test_InterleavedVirtualNonVirtual(self):
161         EXPECTED_RESULT = """  +0 < 16> InterleavedVirtualNonVirtual
162   +0 < 16>     ClassWithVirtualBase ClassWithVirtualBase
163   +0 <  8>         VirtualBaseClass VirtualBaseClass
164   +0 <  8>            __vtbl_ptr_type * _vptr
165   +8 <  1>       bool boolMember
166   +9 <  1>   bool boolMember
167  +10 <  6>   <PADDING: 6 bytes>
168 Total byte size: 16
169 Total pad bytes: 6
170 Padding percentage: 37.50 %"""
171         actual_layout = debugger_instance.layout_for_classname('InterleavedVirtualNonVirtual')
172         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
173
174     def serial_test_ClassWithTwoVirtualBaseClasses(self):
175         EXPECTED_RESULT = """  +0 < 24> ClassWithTwoVirtualBaseClasses
176   +0 <  8>     VirtualBaseClass VirtualBaseClass
177   +0 <  8>        __vtbl_ptr_type * _vptr
178   +8 <  8>     VirtualBaseClass2 VirtualBaseClass2
179   +8 <  8>        __vtbl_ptr_type * _vptr
180  +16 <  1>   bool boolMember
181  +17 <  7>   <PADDING: 7 bytes>
182 Total byte size: 24
183 Total pad bytes: 7
184 Padding percentage: 29.17 %"""
185         actual_layout = debugger_instance.layout_for_classname('ClassWithTwoVirtualBaseClasses')
186         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
187
188     def serial_test_ClassWithVirtualInheritance(self):
189         EXPECTED_RESULT = """  +0 < 64> ClassWithVirtualInheritance
190   +0 < 32>     VirtualInheritingA VirtualInheritingA
191   +0 <  8>        __vtbl_ptr_type * _vptr
192   +8 <  4>       int intMemberA
193  +12 <  4>   <PADDING: 4 bytes>
194  +16 < 40>     VirtualInheritingB VirtualInheritingB
195  +16 <  8>        __vtbl_ptr_type * _vptr
196  +24 <  8>         BasicClassLayout BasicClassLayout
197  +24 <  4>           int intMember
198  +28 <  1>           bool boolMember
199  +45 <  3>       <PADDING: 3 bytes>
200  +32 <  4>       int intMemberB
201  +36 <  4>   <PADDING: 4 bytes>
202  +40 <  8>   double derivedMember
203  +48 < 16>     VirtualBase VirtualBase
204  +48 <  8>        __vtbl_ptr_type * _vptr
205  +56 <  1>       bool baseMember
206  +57 <  7>   <PADDING: 7 bytes>
207 Total byte size: 64
208 Total pad bytes: 18
209 Padding percentage: 28.12 %"""
210         actual_layout = debugger_instance.layout_for_classname('ClassWithVirtualInheritance')
211         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
212
213     def serial_test_ClassWithInheritanceAndClassMember(self):
214         EXPECTED_RESULT = """  +0 < 80> ClassWithInheritanceAndClassMember
215   +0 < 32>     VirtualInheritingA VirtualInheritingA
216   +0 <  8>        __vtbl_ptr_type * _vptr
217   +8 <  4>       int intMemberA
218  +12 <  4>   <PADDING: 4 bytes>
219  +16 < 40>     VirtualInheritingB dataMember
220  +16 <  8>        __vtbl_ptr_type * _vptr
221  +24 <  8>         BasicClassLayout BasicClassLayout
222  +24 <  4>           int intMember
223  +28 <  1>           bool boolMember
224  +45 <  3>       <PADDING: 3 bytes>
225  +32 <  4>       int intMemberB
226  +52 <  4>       <PADDING: 4 bytes>
227  +40 < 16>         VirtualBase VirtualBase
228  +40 <  8>            __vtbl_ptr_type * _vptr
229  +48 <  1>           bool baseMember
230  +49 <  7>   <PADDING: 7 bytes>
231  +56 <  8>   double derivedMember
232  +64 < 16>     VirtualBase VirtualBase
233  +64 <  8>        __vtbl_ptr_type * _vptr
234  +72 <  1>       bool baseMember
235  +73 <  7>   <PADDING: 7 bytes>
236 Total byte size: 80
237 Total pad bytes: 25
238 Padding percentage: 31.25 %"""
239         actual_layout = debugger_instance.layout_for_classname('ClassWithInheritanceAndClassMember')
240         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
241
242     def serial_test_DerivedClassWithIndirectVirtualInheritance(self):
243         EXPECTED_RESULT = """  +0 < 72> DerivedClassWithIndirectVirtualInheritance
244   +0 < 64>     ClassWithVirtualInheritance ClassWithVirtualInheritance
245   +0 < 32>         VirtualInheritingA VirtualInheritingA
246   +0 <  8>            __vtbl_ptr_type * _vptr
247   +8 <  4>           int intMemberA
248  +12 <  4>       <PADDING: 4 bytes>
249  +16 < 40>         VirtualInheritingB VirtualInheritingB
250  +16 <  8>            __vtbl_ptr_type * _vptr
251  +24 <  8>             BasicClassLayout BasicClassLayout
252  +24 <  4>               int intMember
253  +28 <  1>               bool boolMember
254  +45 <  3>           <PADDING: 3 bytes>
255  +32 <  4>           int intMemberB
256  +36 <  4>       <PADDING: 4 bytes>
257  +40 <  8>       double derivedMember
258  +48 <  8>   long mostDerivedMember
259  +56 < 16>     VirtualBase VirtualBase
260  +56 <  8>        __vtbl_ptr_type * _vptr
261  +64 <  1>       bool baseMember
262  +65 <  7>   <PADDING: 7 bytes>
263 Total byte size: 72
264 Total pad bytes: 18
265 Padding percentage: 25.00 %"""
266         actual_layout = debugger_instance.layout_for_classname('DerivedClassWithIndirectVirtualInheritance')
267         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
268
269     def serial_test_ClassWithClassMembers(self):
270         EXPECTED_RESULT = """  +0 < 72> ClassWithClassMembers
271   +0 <  1>   bool boolMember
272   +1 <  3>   <PADDING: 3 bytes>
273   +4 <  8>     BasicClassLayout classMember
274   +4 <  4>       int intMember
275   +8 <  1>       bool boolMember
276   +9 <  7>   <PADDING: 7 bytes>
277  +16 < 24>     ClassWithTwoVirtualBaseClasses virtualClassesMember
278  +16 <  8>         VirtualBaseClass VirtualBaseClass
279  +16 <  8>            __vtbl_ptr_type * _vptr
280  +24 <  8>         VirtualBaseClass2 VirtualBaseClass2
281  +24 <  8>            __vtbl_ptr_type * _vptr
282  +32 <  1>       bool boolMember
283  +33 <  7>   <PADDING: 7 bytes>
284  +40 <  8>   double doubleMember
285  +48 < 16>     ClassWithVirtualBase virtualClassMember
286  +48 <  8>         VirtualBaseClass VirtualBaseClass
287  +48 <  8>            __vtbl_ptr_type * _vptr
288  +56 <  1>       bool boolMember
289  +57 <  7>   <PADDING: 7 bytes>
290  +64 <  4>   int intMember
291  +68 <  4>   <PADDING: 4 bytes>
292 Total byte size: 72
293 Total pad bytes: 28
294 Padding percentage: 38.89 %"""
295         actual_layout = debugger_instance.layout_for_classname('ClassWithClassMembers')
296         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
297
298     def serial_test_ClassWithBitfields(self):
299         EXPECTED_RESULT = """  +0 < 12> ClassWithBitfields
300   +0 <  1>   bool boolMember
301   +1 <  4>   unsigned int bitfield1 : 1
302   +1 <  4>   unsigned int bitfield2 : 2
303   +1 <  4>   unsigned int bitfield3 : 1
304   +1 <  1>   bool bitfield4 : 1
305   +1 <  1>   bool bitfield5 : 2
306   +1 <  1>   bool bitfield6 : 1
307   +2 <  2>   <PADDING: 2 bytes>
308   +4 <  4>   int intMember
309   +8 <  4>   unsigned int bitfield7 : 1
310   +8 <  1>   bool bitfield8 : 1
311   +9 <  3>   <PADDING: 3 bytes>
312 Total byte size: 12
313 Total pad bytes: 5
314 Padding percentage: 41.67 %"""
315         actual_layout = debugger_instance.layout_for_classname('ClassWithBitfields')
316         self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())