blob: 5b36aa7f6d47f042645d99009f4d3bbe358e5724 [file] [log] [blame]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import atexit
import difflib
import lldb
import os
import sys
import unittest
from lldb_dump_class_layout import LLDBDebuggerInstance, ClassLayoutBase
from webkitpy.common.system.systemhost import SystemHost
# Run these tests with ./Tools/Scripts/test-webkitpy dump_class_layout_unittest
# Run a single test with e.g. ./Tools/Scripts/test-webkitpy dump_class_layout_unittest.TestDumpClassLayout.serial_test_ClassWithUniquePtrs
# Compare with clang's output: clang++ -Xclang -fdump-record-layouts DumpClassLayoutTesting.cpp
debugger_instance = None
@atexit.register
def destroy_cached_debug_session():
debugger_instance = None
class TestDumpClassLayout(unittest.TestCase):
@classmethod
def shouldSkip(cls):
return not SystemHost().platform.is_mac()
@classmethod
def setUpClass(cls):
global debugger_instance
if not debugger_instance:
lldbWebKitTesterExecutable = str(os.environ['LLDB_WEBKIT_TESTER_EXECUTABLE'])
architecture = 'x86_64'
debugger_instance = LLDBDebuggerInstance(lldbWebKitTesterExecutable, architecture)
if not debugger_instance:
print('Failed to create lldb debugger instance for %s' % (lldbWebKitTesterExecutable))
def setUp(self):
super(TestDumpClassLayout, self).setUp()
self.maxDiff = None
self.addTypeEqualityFunc(str, self.assertMultiLineEqual)
def serial_test_BasicClassLayout(self):
EXPECTED_RESULT = """ +0 < 8> BasicClassLayout
+0 < 4> int intMember
+4 < 1> bool boolMember
+5 < 3> <PADDING: 3 bytes>
Total byte size: 8
Total pad bytes: 3
Padding percentage: 37.50 %"""
actual_layout = debugger_instance.layout_for_classname('BasicClassLayout')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_PaddingBetweenClassMembers(self):
EXPECTED_RESULT = """ +0 < 16> PaddingBetweenClassMembers
+0 < 8> BasicClassLayout basic1
+0 < 4> int intMember
+4 < 1> bool boolMember
+5 < 3> <PADDING: 3 bytes>
+8 < 8> BasicClassLayout basic2
+8 < 4> int intMember
+12 < 1> bool boolMember
+13 < 3> <PADDING: 3 bytes>
Total byte size: 16
Total pad bytes: 6
Padding percentage: 37.50 %"""
actual_layout = debugger_instance.layout_for_classname('PaddingBetweenClassMembers')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_BoolPaddingClass(self):
EXPECTED_RESULT = """ +0 < 12> BoolPaddingClass
+0 < 1> bool bool1
+1 < 1> bool bool2
+2 < 1> bool bool3
+3 < 1> <PADDING: 1 byte>
+4 < 8> BoolMemberFirst memberClass
+4 < 1> bool boolMember
+5 < 3> <PADDING: 3 bytes>
+8 < 4> int intMember
Total byte size: 12
Total pad bytes: 4
Padding percentage: 33.33 %"""
actual_layout = debugger_instance.layout_for_classname('BoolPaddingClass')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_ClassWithEmptyClassMembers(self):
EXPECTED_RESULT = """ +0 < 12> ClassWithEmptyClassMembers
+0 < 4> int intMember
+4 < 1> EmptyClass empty1
+4 < 1> <PADDING: 1 byte>
+5 < 1> bool boolMember
+6 < 1> EmptyClass empty2
+6 < 2> <PADDING: 2 bytes>
+8 < 4> int intMember2
Total byte size: 12
Total pad bytes: 3
Padding percentage: 25.00 %"""
actual_layout = debugger_instance.layout_for_classname('ClassWithEmptyClassMembers')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_SimpleVirtualClass(self):
EXPECTED_RESULT = """ +0 < 24> SimpleVirtualClass
+0 < 8> __vtbl_ptr_type * _vptr
+8 < 4> int intMember
+12 < 4> <PADDING: 4 bytes>
+16 < 8> double doubleMember
Total byte size: 24
Total pad bytes: 4
Padding percentage: 16.67 %"""
actual_layout = debugger_instance.layout_for_classname('SimpleVirtualClass')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_VirtualClassWithNonVirtualBase(self):
EXPECTED_RESULT = """ +0 < 24> VirtualClassWithNonVirtualBase
+0 < 8> __vtbl_ptr_type * _vptr
+8 < 8> BasicClassLayout BasicClassLayout
+8 < 4> int intMember
+12 < 1> bool boolMember
+13 < 3> <PADDING: 3 bytes>
+16 < 8> double doubleMember
Total byte size: 24
Total pad bytes: 3
Padding percentage: 12.50 %"""
actual_layout = debugger_instance.layout_for_classname('VirtualClassWithNonVirtualBase')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_InterleavedVirtualNonVirtual(self):
EXPECTED_RESULT = """ +0 < 16> InterleavedVirtualNonVirtual
+0 < 16> ClassWithVirtualBase ClassWithVirtualBase
+0 < 8> VirtualBaseClass VirtualBaseClass
+0 < 8> __vtbl_ptr_type * _vptr
+8 < 1> bool boolMember
+9 < 1> bool boolMember
+10 < 6> <PADDING: 6 bytes>
Total byte size: 16
Total pad bytes: 6
Padding percentage: 37.50 %"""
actual_layout = debugger_instance.layout_for_classname('InterleavedVirtualNonVirtual')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_ClassWithTwoVirtualBaseClasses(self):
EXPECTED_RESULT = """ +0 < 24> ClassWithTwoVirtualBaseClasses
+0 < 8> VirtualBaseClass VirtualBaseClass
+0 < 8> __vtbl_ptr_type * _vptr
+8 < 8> VirtualBaseClass2 VirtualBaseClass2
+8 < 8> __vtbl_ptr_type * _vptr
+16 < 1> bool boolMember
+17 < 7> <PADDING: 7 bytes>
Total byte size: 24
Total pad bytes: 7
Padding percentage: 29.17 %"""
actual_layout = debugger_instance.layout_for_classname('ClassWithTwoVirtualBaseClasses')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_ClassWithVirtualInheritance(self):
EXPECTED_RESULT = """ +0 < 64> ClassWithVirtualInheritance
+0 < 32> VirtualInheritingA VirtualInheritingA
+0 < 8> __vtbl_ptr_type * _vptr
+8 < 4> int intMemberA
+12 < 4> <PADDING: 4 bytes>
+16 < 40> VirtualInheritingB VirtualInheritingB
+16 < 8> __vtbl_ptr_type * _vptr
+24 < 8> BasicClassLayout BasicClassLayout
+24 < 4> int intMember
+28 < 1> bool boolMember
+29 < 3> <PADDING: 3 bytes>
+32 < 4> int intMemberB
+36 < 4> <PADDING: 4 bytes>
+40 < 8> double derivedMember
+48 < 16> VirtualBase VirtualBase
+48 < 8> __vtbl_ptr_type * _vptr
+56 < 1> bool baseMember
+57 < 7> <PADDING: 7 bytes>
Total byte size: 64
Total pad bytes: 18
Padding percentage: 28.12 %"""
actual_layout = debugger_instance.layout_for_classname('ClassWithVirtualInheritance')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_ClassWithInheritanceAndClassMember(self):
EXPECTED_RESULT = """ +0 < 80> ClassWithInheritanceAndClassMember
+0 < 32> VirtualInheritingA VirtualInheritingA
+0 < 8> __vtbl_ptr_type * _vptr
+8 < 4> int intMemberA
+12 < 4> <PADDING: 4 bytes>
+16 < 40> VirtualInheritingB dataMember
+16 < 8> __vtbl_ptr_type * _vptr
+24 < 8> BasicClassLayout BasicClassLayout
+24 < 4> int intMember
+28 < 1> bool boolMember
+29 < 3> <PADDING: 3 bytes>
+32 < 4> int intMemberB
+36 < 4> <PADDING: 4 bytes>
+40 < 16> VirtualBase VirtualBase
+40 < 8> __vtbl_ptr_type * _vptr
+48 < 1> bool baseMember
+49 < 7> <PADDING: 7 bytes>
+56 < 8> double derivedMember
+64 < 16> VirtualBase VirtualBase
+64 < 8> __vtbl_ptr_type * _vptr
+72 < 1> bool baseMember
+73 < 7> <PADDING: 7 bytes>
Total byte size: 80
Total pad bytes: 25
Padding percentage: 31.25 %"""
actual_layout = debugger_instance.layout_for_classname('ClassWithInheritanceAndClassMember')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_DerivedClassWithIndirectVirtualInheritance(self):
EXPECTED_RESULT = """ +0 < 72> DerivedClassWithIndirectVirtualInheritance
+0 < 64> ClassWithVirtualInheritance ClassWithVirtualInheritance
+0 < 32> VirtualInheritingA VirtualInheritingA
+0 < 8> __vtbl_ptr_type * _vptr
+8 < 4> int intMemberA
+12 < 4> <PADDING: 4 bytes>
+16 < 40> VirtualInheritingB VirtualInheritingB
+16 < 8> __vtbl_ptr_type * _vptr
+24 < 8> BasicClassLayout BasicClassLayout
+24 < 4> int intMember
+28 < 1> bool boolMember
+29 < 3> <PADDING: 3 bytes>
+32 < 4> int intMemberB
+36 < 4> <PADDING: 4 bytes>
+40 < 8> double derivedMember
+48 < 8> long mostDerivedMember
+56 < 16> VirtualBase VirtualBase
+56 < 8> __vtbl_ptr_type * _vptr
+64 < 1> bool baseMember
+65 < 7> <PADDING: 7 bytes>
Total byte size: 72
Total pad bytes: 18
Padding percentage: 25.00 %"""
actual_layout = debugger_instance.layout_for_classname('DerivedClassWithIndirectVirtualInheritance')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_ClassWithClassMembers(self):
EXPECTED_RESULT = """ +0 < 72> ClassWithClassMembers
+0 < 1> bool boolMember
+1 < 3> <PADDING: 3 bytes>
+4 < 8> BasicClassLayout classMember
+4 < 4> int intMember
+8 < 1> bool boolMember
+9 < 7> <PADDING: 7 bytes>
+16 < 24> ClassWithTwoVirtualBaseClasses virtualClassesMember
+16 < 8> VirtualBaseClass VirtualBaseClass
+16 < 8> __vtbl_ptr_type * _vptr
+24 < 8> VirtualBaseClass2 VirtualBaseClass2
+24 < 8> __vtbl_ptr_type * _vptr
+32 < 1> bool boolMember
+33 < 7> <PADDING: 7 bytes>
+40 < 8> double doubleMember
+48 < 16> ClassWithVirtualBase virtualClassMember
+48 < 8> VirtualBaseClass VirtualBaseClass
+48 < 8> __vtbl_ptr_type * _vptr
+56 < 1> bool boolMember
+57 < 7> <PADDING: 7 bytes>
+64 < 4> int intMember
+68 < 4> <PADDING: 4 bytes>
Total byte size: 72
Total pad bytes: 28
Padding percentage: 38.89 %"""
actual_layout = debugger_instance.layout_for_classname('ClassWithClassMembers')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_ClassWithBitfields(self):
EXPECTED_RESULT = """ +0 < 12> ClassWithBitfields
+0 < 1> bool boolMember
+1 < :1> unsigned int bitfield1 : 1
+1 < :2> unsigned int bitfield2 : 2
+1 < :1> unsigned int bitfield3 : 1
+1 < :1> bool bitfield4 : 1
+1 < :2> bool bitfield5 : 2
+1 < :1> bool bitfield6 : 1
+2 < 2> <PADDING: 2 bytes>
+4 < 4> int intMember
+8 < :1> unsigned int bitfield7 : 1
+8 < :1> bool bitfield8 : 1
+8 < :6> <UNUSED BITS: 6 bits>
+9 < 3> <PADDING: 3 bytes>
Total byte size: 12
Total pad bytes: 5
Padding percentage: 41.67 %"""
actual_layout = debugger_instance.layout_for_classname('ClassWithBitfields')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_ClassWithPaddedBitfields(self):
EXPECTED_RESULT = """ +0 < 16> ClassWithPaddedBitfields
+0 < 1> bool boolMember
+1 < :1> unsigned int bitfield1 : 1
+1 < :1> bool bitfield2 : 1
+1 < :2> unsigned int bitfield3 : 2
+1 < :1> unsigned int bitfield4 : 1
+1 < :2> unsigned long bitfield5 : 2
+1 < :1> <UNUSED BITS: 1 bit>
+2 < 1> <PADDING: 1 bytes>
+4 < 4> int intMember
+8 < :1> unsigned int bitfield7 : 1
+8 < :9> unsigned int bitfield8 : 9
+9 < :1> bool bitfield9 : 1
+9 < :5> <UNUSED BITS: 5 bits>
+10 < 6> <PADDING: 6 bytes>
Total byte size: 16
Total pad bytes: 7
Padding percentage: 49.22 %"""
actual_layout = debugger_instance.layout_for_classname('ClassWithPaddedBitfields')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_MemberHasBitfieldPadding(self):
EXPECTED_RESULT = """ +0 < 24> MemberHasBitfieldPadding
+0 < 16> ClassWithPaddedBitfields bitfieldMember
+0 < 1> bool boolMember
+1 < :1> unsigned int bitfield1 : 1
+1 < :1> bool bitfield2 : 1
+1 < :2> unsigned int bitfield3 : 2
+1 < :1> unsigned int bitfield4 : 1
+1 < :2> unsigned long bitfield5 : 2
+1 < :1> <UNUSED BITS: 1 bit>
+2 < 1> <PADDING: 1 bytes>
+4 < 4> int intMember
+8 < :1> unsigned int bitfield7 : 1
+8 < :9> unsigned int bitfield8 : 9
+9 < :1> bool bitfield9 : 1
+9 < :5> <UNUSED BITS: 5 bits>
+10 < 6> <PADDING: 6 bytes>
+16 < :1> bool bitfield1 : 1
+16 < :7> <UNUSED BITS: 7 bits>
+17 < 7> <PADDING: 7 bytes>
Total byte size: 24
Total pad bytes: 14
Padding percentage: 61.98 %"""
actual_layout = debugger_instance.layout_for_classname('MemberHasBitfieldPadding')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())
def serial_test_InheritsFromClassWithPaddedBitfields(self):
EXPECTED_RESULT = """ +0 < 16> InheritsFromClassWithPaddedBitfields
+0 < 16> ClassWithPaddedBitfields ClassWithPaddedBitfields
+0 < 1> bool boolMember
+1 < :1> unsigned int bitfield1 : 1
+1 < :1> bool bitfield2 : 1
+1 < :2> unsigned int bitfield3 : 2
+1 < :1> unsigned int bitfield4 : 1
+1 < :2> unsigned long bitfield5 : 2
+1 < :1> <UNUSED BITS: 1 bit>
+2 < 1> <PADDING: 1 bytes>
+4 < 4> int intMember
+8 < :1> unsigned int bitfield7 : 1
+8 < :9> unsigned int bitfield8 : 9
+9 < :1> bool bitfield9 : 1
+9 < :5> <UNUSED BITS: 5 bits>
+10 < :1> bool derivedBitfield : 1
+10 < :7> <UNUSED BITS: 7 bits>
+11 < 5> <PADDING: 5 bytes>
Total byte size: 16
Total pad bytes: 6
Padding percentage: 42.97 %"""
actual_layout = debugger_instance.layout_for_classname('InheritsFromClassWithPaddedBitfields')
self.assertEqual(EXPECTED_RESULT, actual_layout.as_string())