384 lines
15 KiB
Python
Executable File
384 lines
15 KiB
Python
Executable File
#!/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())
|