Diff
Modified: trunk/Tools/ChangeLog (233613 => 233614)
--- trunk/Tools/ChangeLog 2018-07-07 07:10:20 UTC (rev 233613)
+++ trunk/Tools/ChangeLog 2018-07-07 15:52:49 UTC (rev 233614)
@@ -1,3 +1,93 @@
+2018-06-30 Simon Fraser <[email protected]>
+
+ dump-class-layout reports wrong padding in many cases
+ https://bugs.webkit.org/show_bug.cgi?id=185801
+
+ Reviewed by Dan Bates.
+
+ Enhance dump-class-layout, fixing bugs and adding unit tests. This also includes
+ the patch from bug 187141.
+
+ Move the code that uses lldb to fetch the class layout to its own module,
+ lldb_dump_class_layout.py, so it can be unit tested. Change this code
+ to build up a data structure for the class. This is necessary since correct
+ padding computation is easier with second traversal over the data structure.
+
+ Try to deal with the empty base class optimization, which is necessary to report
+ correct padding in classes using std::unique_ptr, and correctly handle virtual base classes.
+
+ The ClassLayoutBase class knows how to test for equality and generate a string
+ or string list representation of itself. The derived ClassLayout class knows
+ how to build up the class layout using the lldb Python bindings.
+
+ Finally we wrap the lldb debugger instance in LLDBDebuggerInstance so that we don't
+ have to make a new one for each unit test. The tests have to run serially.
+
+ Add to lldbWebKitTester a .cpp file that contains a set of C++ classes to unit-test
+ dump-class-layout.
+
+ * Scripts/dump-class-layout:
+ (main):
+ * lldb/dump_class_layout_unittest.py:
+ (destroy_cached_debug_session):
+ (TestDumpClassLayout):
+ (TestDumpClassLayout.setUpClass):
+ (TestDumpClassLayout.setUp):
+ (TestDumpClassLayout.serial_test_BasicClassLayout):
+ (serial_test_PaddingBetweenClassMembers):
+ (serial_test_BoolPaddingClass):
+ (serial_test_ClassWithEmptyClassMembers):
+ (serial_test_SimpleVirtualClass):
+ (serial_test_SimpleVirtualClassWithNonVirtualBase):
+ (serial_test_InterleavedVirtualNonVirtual):
+ (serial_test_ClassWithTwoVirtualBaseClasses):
+ (serial_test_ClassWithClassMembers):
+ (serial_test_ClassWithBitfields):
+ (serial_test_ClassWithUniquePtrs):
+ (serial_test_ClassWithOptionals):
+ (TestDumpClassLayout.test_BasicClassLayout): Deleted.
+ * lldb/lldbWebKitTester/DumpClassLayoutTesting.cpp:
+ (EmptyClass::doStuff):
+ (VirtualBaseClass::~VirtualBaseClass):
+ (VirtualBaseClass2::~VirtualBaseClass2):
+ (SimpleVirtualClass::~SimpleVirtualClass):
+ (SimpleVirtualClass::doStuff):
+ (SimpleVirtualClassWithNonVirtualBase::~SimpleVirtualClassWithNonVirtualBase):
+ (SimpleVirtualClassWithNonVirtualBase::doStuff):
+ (avoidClassDeadStripping):
+ * lldb/lldb_dump_class_layout.py:
+ (ansi_colors):
+ (ClassLayoutBase):
+ (ClassLayoutBase.__init__):
+ (ClassLayoutBase.__ne__):
+ (ClassLayoutBase.__eq__):
+ (ClassLayoutBase._to_string_recursive):
+ (ClassLayoutBase.as_string_list):
+ (ClassLayoutBase.as_string):
+ (ClassLayoutBase.dump):
+ (ClassLayoutExpected):
+ (ClassLayoutExpected.__init__):
+ (ClassLayout):
+ (ClassLayout.__init__):
+ (ClassLayout._has_polymorphic_base_class):
+ (ClassLayout._parse):
+ (ClassLayout._probably_has_empty_base_class_optimization):
+ (ClassLayout._compute_padding_recursive):
+ (ClassLayout._compute_padding):
+ (LLDBDebuggerInstance):
+ (LLDBDebuggerInstance.__init__):
+ (LLDBDebuggerInstance.__del__):
+ (LLDBDebuggerInstance._get_first_file_architecture):
+ (LLDBDebuggerInstance.layout_for_classname):
+ (ClassLayoutDumper): Deleted.
+ (ClassLayoutDumper.__init__): Deleted.
+ (ClassLayoutDumper._get_first_file_architecture): Deleted.
+ (ClassLayoutDumper.verify_type): Deleted.
+ (ClassLayoutDumper.verify_type_recursive): Deleted.
+ (ClassLayoutDumper._class_layout_as_string): Deleted.
+ (ClassLayoutDumper.dump_to_string): Deleted.
+ (ClassLayoutDumper.dump): Deleted.
+
2018-07-06 Christopher Reid <[email protected]>
[WinCairo] WebKit MiniBrowser crashes when attempting to navigate to certain URLs
Modified: trunk/Tools/Scripts/dump-class-layout (233613 => 233614)
--- trunk/Tools/Scripts/dump-class-layout 2018-07-07 07:10:20 UTC (rev 233613)
+++ trunk/Tools/Scripts/dump-class-layout 2018-07-07 15:52:49 UTC (rev 233614)
@@ -31,10 +31,12 @@
import subprocess
from sets import Set
-from webkitpy.common.system.systemhost import SystemHost
-sys.path.append(SystemHost().path_to_lldb_python_directory())
-import lldb
+up = os.path.dirname
+tools_directory = up(up(os.path.abspath(__file__)))
+sys.path.insert(0, os.path.join(tools_directory, "lldb"))
+from lldb_dump_class_layout import LLDBDebuggerInstance, ClassLayout
+
framework = "WebCore"
build_directory = ""
config = "Release"
@@ -44,120 +46,6 @@
scriptpath = os.path.dirname(os.path.realpath(__file__))
return subprocess.check_output([os.path.join(scriptpath, "webkit-build-directory"), "--top-level"]).strip()
-def verify_type(target, type):
- typename = type.GetName()
- seenOffset = Set()
- (end_offset, padding) = verify_type_recursive(target, type, None, 0, 0, 0, seenOffset)
- byte_size = type.GetByteSize()
- print 'Total byte size: %u' % (byte_size)
- print 'Total pad bytes: %u' % (padding)
- if padding > 0:
- print 'Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0)
- print
-
-def verify_type_recursive(target, type, member_name, depth, base_offset, padding, seenOffset):
- prev_end_offset = base_offset
- typename = type.GetName()
- byte_size = type.GetByteSize()
- if member_name and member_name != typename:
- print '%+4u <%3u> %s%s %s;' % (base_offset, byte_size, ' ' * depth, typename, member_name)
- else:
- print '%+4u {%3u} %s%s' % (base_offset, byte_size, ' ' * depth, typename)
-
- members = type.members
- if members:
- for member_idx, member in enumerate(members):
- member_type = member.GetType()
- member_canonical_type = member_type.GetCanonicalType()
- member_type_class = member_canonical_type.GetTypeClass()
- member_name = member.GetName()
- member_offset = member.GetOffsetInBytes()
- member_total_offset = member_offset + base_offset
- member_byte_size = member_type.GetByteSize()
- member_is_class_or_struct = False
-
- if (member_offset, member_name) in seenOffset:
- continue
- seenOffset.add((member_offset, member_name))
-
- if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass:
- member_is_class_or_struct = True
- if member_idx == 0 and member_offset == target.GetAddressByteSize() and type.IsPolymorphicClass():
- ptr_size = target.GetAddressByteSize()
- print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))
- prev_end_offset = ptr_size
- else:
- if prev_end_offset < member_total_offset:
- member_padding = member_total_offset - prev_end_offset
- padding = padding + member_padding
- print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1))
-
- if member_is_class_or_struct:
- (prev_end_offset, padding) = verify_type_recursive(target, member_canonical_type, member_name, depth + 1, member_total_offset, padding, seenOffset)
- else:
- prev_end_offset = member_total_offset + member_byte_size
- member_typename = member_type.GetName()
- if member.IsBitfield():
- print '%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member.GetBitfieldSizeInBits(), member_name)
- else:
- print '%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member_name)
-
- if prev_end_offset < byte_size:
- last_member_padding = byte_size - prev_end_offset
- print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, last_member_padding, ' ' * (depth + 1))
- padding += last_member_padding
- else:
- if type.IsPolymorphicClass():
- ptr_size = target.GetAddressByteSize()
- print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))
- prev_end_offset = ptr_size
- prev_end_offset = base_offset + byte_size
-
- return (prev_end_offset, padding)
-
-def get_first_file_architecture(framework_path):
- p = re.compile('shared library +(\w+)$')
- file_result = subprocess.check_output(["file", framework_path]).split('\n')
- arches = []
- for line in file_result:
- match = p.search(line)
- if match:
- arches.append(match.group(1));
-
- if len(arches) > 1:
- print 'Found architectures %s, using %s' % (arches, arches[0])
-
- if len(arches) > 0:
- return arches[0]
-
- return lldb.LLDB_ARCH_DEFAULT
-
-def dump_class(framework_path, classname, architecture):
- debugger = lldb.SBDebugger.Create()
- debugger.SetAsync(False)
- if not architecture:
- architecture = get_first_file_architecture(framework_path)
-
- target = debugger.CreateTargetWithFileAndArch(framework_path, architecture)
- if not target:
- print "Failed to make target for " + framework_path;
- sys.exit(1)
-
- module = target.GetModuleAtIndex(0)
- if not module:
- print "Failed to get first module in " + framework_path;
- sys.exit(1)
-
- types = module.FindTypes(classname)
- if types.GetSize():
- print 'Found %u types matching "%s" in "%s" for %s' % (len(types), classname, module.file, architecture)
- for type in types:
- verify_type(target, type)
- else:
- print 'error: no type matches "%s" in "%s"' % (classname, module.file)
-
- lldb.SBDebugger.Destroy(debugger)
-
def main():
parser = argparse.ArgumentParser(description='Dumps the in-memory layout of the given class or classes, showing padding holes.')
parser.add_argument('framework', metavar='framework',
@@ -184,7 +72,11 @@
build_dir = args.build_directory
target_path = os.path.join(build_dir, args.config, args.framework + ".framework", args.framework);
- dump_class(target_path, args.classname, args.arch)
+
+ lldb_instance = LLDBDebuggerInstance(target_path, args.arch)
+ class_layout = lldb_instance.layout_for_classname(args.classname)
+ class_layout.dump()
+
if __name__ == "__main__":
main()
Modified: trunk/Tools/Scripts/webkitpy/test/main.py (233613 => 233614)
--- trunk/Tools/Scripts/webkitpy/test/main.py 2018-07-07 07:10:20 UTC (rev 233613)
+++ trunk/Tools/Scripts/webkitpy/test/main.py 2018-07-07 15:52:49 UTC (rev 233614)
@@ -291,6 +291,12 @@
test_method_prefixes = []
def getTestCaseNames(self, testCaseClass):
+ should_skip_class_method = getattr(testCaseClass, "shouldSkip", None)
+ if callable(should_skip_class_method):
+ if testCaseClass.shouldSkip():
+ _log.info('Skipping tests in %s' % (testCaseClass.__name__))
+ return []
+
def isTestMethod(attrname, testCaseClass=testCaseClass):
if not hasattr(getattr(testCaseClass, attrname), '__call__'):
return False
Added: trunk/Tools/lldb/dump_class_layout_unittest.py (0 => 233614)
--- trunk/Tools/lldb/dump_class_layout_unittest.py (rev 0)
+++ trunk/Tools/lldb/dump_class_layout_unittest.py 2018-07-07 15:52:49 UTC (rev 233614)
@@ -0,0 +1,316 @@
+#!/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 webkitpy.common.system.systemhost import SystemHost
+from webkitpy.port.config import Config
+
+from lldb_dump_class_layout import LLDBDebuggerInstance, ClassLayoutBase
+
+_host = 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
+
+
[email protected]
+def destroy_cached_debug_session():
+ debugger_instance = None
+
+
+class TestDumpClassLayout(unittest.TestCase):
+ @classmethod
+ def shouldSkip(cls):
+ return not _host.platform.is_mac()
+
+ @classmethod
+ def setUpClass(cls):
+ global debugger_instance
+ if not debugger_instance:
+ LLDB_WEBKIT_TESTER_NAME = 'lldbWebKitTester'
+
+ config = Config(_host.executive, _host.filesystem)
+ lldbWebKitTesterExecutable = os.path.join(config.build_directory(config.default_configuration()), LLDB_WEBKIT_TESTER_NAME)
+
+ 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
+ +9 < 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
+ +45 < 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
+ +45 < 3> <PADDING: 3 bytes>
+ +32 < 4> int intMemberB
+ +52 < 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
+ +45 < 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 < 4> unsigned int bitfield1 : 1
+ +1 < 4> unsigned int bitfield2 : 2
+ +1 < 4> unsigned int bitfield3 : 1
+ +1 < 1> bool bitfield4 : 1
+ +1 < 1> bool bitfield5 : 2
+ +1 < 1> bool bitfield6 : 1
+ +2 < 2> <PADDING: 2 bytes>
+ +4 < 4> int intMember
+ +8 < 4> unsigned int bitfield7 : 1
+ +8 < 1> bool bitfield8 : 1
+ +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())
Property changes on: trunk/Tools/lldb/dump_class_layout_unittest.py
___________________________________________________________________
Added: svn:executable
+*
\ No newline at end of property
Added: trunk/Tools/lldb/lldbWebKitTester/DumpClassLayoutTesting.cpp (0 => 233614)
--- trunk/Tools/lldb/lldbWebKitTester/DumpClassLayoutTesting.cpp (rev 0)
+++ trunk/Tools/lldb/lldbWebKitTester/DumpClassLayoutTesting.cpp 2018-07-07 15:52:49 UTC (rev 233614)
@@ -0,0 +1,456 @@
+/*
+ * 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.
+ */
+
+#include "DumpClassLayoutTesting.h"
+
+#include <memory>
+#include <wtf/Optional.h>
+
+/*
+*** Dumping AST Record Layout
+ 0 | class BasicClassLayout
+ 0 | int intMember
+ 4 | _Bool boolMember
+ | [sizeof=8, dsize=5, align=4,
+ | nvsize=5, nvalign=4]
+*/
+class BasicClassLayout {
+ int intMember;
+ bool boolMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class PaddingBetweenClassMembers
+ 0 | class BasicClassLayout basic1
+ 0 | int intMember
+ 4 | _Bool boolMember
+ 8 | class BasicClassLayout basic2
+ 8 | int intMember
+ 12 | _Bool boolMember
+ | [sizeof=16, dsize=16, align=4,
+ | nvsize=16, nvalign=4]
+*/
+class PaddingBetweenClassMembers {
+ BasicClassLayout basic1;
+ BasicClassLayout basic2;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class BoolMemberFirst
+ 0 | _Bool boolMember
+ 4 | int intMember
+ | [sizeof=8, dsize=8, align=4,
+ | nvsize=8, nvalign=4]
+*/
+class BoolMemberFirst {
+ bool boolMember;
+ int intMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class BoolPaddingClass
+ 0 | _Bool bool1
+ 1 | _Bool bool2
+ 2 | _Bool bool3
+ 4 | class BoolMemberFirst memberClass
+ 4 | _Bool boolMember
+ 8 | int intMember
+ | [sizeof=12, dsize=12, align=4,
+ | nvsize=12, nvalign=4]
+*/
+class BoolPaddingClass {
+ bool bool1;
+ bool bool2;
+ bool bool3;
+ BoolMemberFirst memberClass;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class EmptyClass (empty)
+ | [sizeof=1, dsize=1, align=1,
+ | nvsize=1, nvalign=1]
+*/
+class EmptyClass {
+ void doStuff() { }
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class ClassWithEmptyClassMembers
+ 0 | int intMember
+ 4 | class EmptyClass empty1 (empty)
+ 5 | _Bool boolMember
+ 6 | class EmptyClass empty2 (empty)
+ 8 | int intMember2
+ | [sizeof=12, dsize=12, align=4,
+ | nvsize=12, nvalign=4]
+*/
+class ClassWithEmptyClassMembers {
+ int intMember;
+ EmptyClass empty1;
+ bool boolMember;
+ EmptyClass empty2;
+ int intMember2;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class VirtualBaseClass
+ 0 | (VirtualBaseClass vtable pointer)
+ | [sizeof=8, dsize=8, align=8,
+ | nvsize=8, nvalign=8]
+*/
+class VirtualBaseClass {
+public:
+ virtual ~VirtualBaseClass() { }
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class VirtualBaseClass2
+ 0 | (VirtualBaseClass2 vtable pointer)
+ | [sizeof=8, dsize=8, align=8,
+ | nvsize=8, nvalign=8]
+*/
+class VirtualBaseClass2 {
+public:
+ virtual ~VirtualBaseClass2() { }
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class SimpleVirtualClass
+ 0 | (SimpleVirtualClass vtable pointer)
+ 8 | int intMember
+ 16 | double doubleMember
+ | [sizeof=24, dsize=24, align=8,
+ | nvsize=24, nvalign=8]
+*/
+class SimpleVirtualClass {
+public:
+ virtual ~SimpleVirtualClass() { }
+ virtual void doStuff() { }
+ int intMember;
+ double doubleMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class VirtualClassWithNonVirtualBase
+ 0 | (VirtualClassWithNonVirtualBase vtable pointer)
+ 8 | class BasicClassLayout (base)
+ 8 | int intMember
+ 12 | _Bool boolMember
+ 16 | double doubleMember
+ | [sizeof=24, dsize=24, align=8,
+ | nvsize=24, nvalign=8]
+*/
+class VirtualClassWithNonVirtualBase : public BasicClassLayout {
+public:
+ virtual ~VirtualClassWithNonVirtualBase() { }
+ virtual void doStuff() { }
+ double doubleMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class ClassWithVirtualBase
+ 0 | class VirtualBaseClass (primary base)
+ 0 | (VirtualBaseClass vtable pointer)
+ 8 | _Bool boolMember
+ | [sizeof=16, dsize=9, align=8,
+ | nvsize=9, nvalign=8]
+*/
+class ClassWithVirtualBase: public VirtualBaseClass {
+public:
+ ClassWithVirtualBase() { }
+ virtual void doStuff() { }
+ bool boolMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class InterleavedVirtualNonVirtual
+ 0 | class ClassWithVirtualBase (primary base)
+ 0 | class VirtualBaseClass (primary base)
+ 0 | (VirtualBaseClass vtable pointer)
+ 8 | _Bool boolMember
+ 9 | _Bool boolMember
+ | [sizeof=16, dsize=10, align=8,
+ | nvsize=10, nvalign=8]
+*/
+class InterleavedVirtualNonVirtual: public ClassWithVirtualBase {
+public:
+ InterleavedVirtualNonVirtual() { }
+ virtual ~InterleavedVirtualNonVirtual() { }
+ bool boolMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class ClassWithTwoVirtualBaseClasses
+ 0 | class VirtualBaseClass (primary base)
+ 0 | (VirtualBaseClass vtable pointer)
+ 8 | class VirtualBaseClass2 (base)
+ 8 | (VirtualBaseClass2 vtable pointer)
+ 16 | _Bool boolMember
+ | [sizeof=24, dsize=17, align=8,
+ | nvsize=17, nvalign=8]
+*/
+class ClassWithTwoVirtualBaseClasses: public VirtualBaseClass, VirtualBaseClass2 {
+public:
+ ClassWithTwoVirtualBaseClasses() { }
+ bool boolMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class VirtualBase
+ 0 | (VirtualBase vtable pointer)
+ 8 | _Bool baseMember
+ | [sizeof=16, dsize=9, align=8,
+ | nvsize=9, nvalign=8]
+*/
+class VirtualBase {
+public:
+ virtual ~VirtualBase() { }
+ bool baseMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class VirtualInheritingA
+ 0 | (VirtualInheritingA vtable pointer)
+ 8 | int intMemberA
+ 16 | class VirtualBase (virtual base)
+ 16 | (VirtualBase vtable pointer)
+ 24 | _Bool baseMember
+ | [sizeof=32, dsize=25, align=8,
+ | nvsize=12, nvalign=8]
+*/
+class VirtualInheritingA: virtual public VirtualBase {
+public:
+ VirtualInheritingA() { }
+ int intMemberA;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class VirtualInheritingB
+ 0 | (VirtualInheritingB vtable pointer)
+ 8 | class BasicClassLayout (base)
+ 8 | int intMember
+ 12 | _Bool boolMember
+ 16 | int intMemberB
+ 24 | class VirtualBase (virtual base)
+ 24 | (VirtualBase vtable pointer)
+ 32 | _Bool baseMember
+ | [sizeof=40, dsize=33, align=8,
+ | nvsize=20, nvalign=8]
+*/
+class VirtualInheritingB: public virtual VirtualBase, public BasicClassLayout {
+public:
+ VirtualInheritingB() { }
+ int intMemberB;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class ClassWithVirtualInheritance
+ 0 | class VirtualInheritingA (primary base)
+ 0 | (VirtualInheritingA vtable pointer)
+ 8 | int intMemberA
+ 16 | class VirtualInheritingB (base)
+ 16 | (VirtualInheritingB vtable pointer)
+ 24 | class BasicClassLayout (base)
+ 24 | int intMember
+ 28 | _Bool boolMember
+ 32 | int intMemberB
+ 40 | double derivedMember
+ 48 | class VirtualBase (virtual base)
+ 48 | (VirtualBase vtable pointer)
+ 56 | _Bool baseMember
+ | [sizeof=64, dsize=57, align=8,
+ | nvsize=48, nvalign=8]
+*/
+class ClassWithVirtualInheritance : public VirtualInheritingA, public VirtualInheritingB {
+public:
+ double derivedMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class DerivedClassWithIndirectVirtualInheritance
+ 0 | class ClassWithVirtualInheritance (primary base)
+ 0 | class VirtualInheritingA (primary base)
+ 0 | (VirtualInheritingA vtable pointer)
+ 8 | int intMemberA
+ 16 | class VirtualInheritingB (base)
+ 16 | (VirtualInheritingB vtable pointer)
+ 24 | class BasicClassLayout (base)
+ 24 | int intMember
+ 28 | _Bool boolMember
+ 32 | int intMemberB
+ 40 | double derivedMember
+ 48 | long mostDerivedMember
+ 56 | class VirtualBase (virtual base)
+ 56 | (VirtualBase vtable pointer)
+ 64 | _Bool baseMember
+ | [sizeof=72, dsize=65, align=8,
+ | nvsize=56, nvalign=8]
+*/
+class DerivedClassWithIndirectVirtualInheritance : public ClassWithVirtualInheritance {
+public:
+ long mostDerivedMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class ClassWithInheritanceAndClassMember
+ 0 | class VirtualInheritingA (primary base)
+ 0 | (VirtualInheritingA vtable pointer)
+ 8 | int intMemberA
+ 16 | class VirtualInheritingB dataMember
+ 16 | (VirtualInheritingB vtable pointer)
+ 24 | class BasicClassLayout (base)
+ 24 | int intMember
+ 28 | _Bool boolMember
+ 32 | int intMemberB
+ 40 | class VirtualBase (virtual base)
+ 40 | (VirtualBase vtable pointer)
+ 48 | _Bool baseMember
+ 56 | double derivedMember
+ 64 | class VirtualBase (virtual base)
+ 64 | (VirtualBase vtable pointer)
+ 72 | _Bool baseMember
+ | [sizeof=80, dsize=73, align=8,
+ | nvsize=64, nvalign=8]
+*/
+class ClassWithInheritanceAndClassMember : public VirtualInheritingA {
+public:
+ VirtualInheritingB dataMember;
+ double derivedMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class ClassWithClassMembers
+ 0 | _Bool boolMember
+ 4 | class BasicClassLayout classMember
+ 4 | int intMember
+ 8 | _Bool boolMember
+ 16 | class ClassWithTwoVirtualBaseClasses virtualClassesMember
+ 16 | class VirtualBaseClass (primary base)
+ 16 | (VirtualBaseClass vtable pointer)
+ 24 | class VirtualBaseClass2 (base)
+ 24 | (VirtualBaseClass2 vtable pointer)
+ 32 | _Bool boolMember
+ 40 | double doubleMember
+ 48 | class ClassWithVirtualBase virtualClassMember
+ 48 | class VirtualBaseClass (primary base)
+ 48 | (VirtualBaseClass vtable pointer)
+ 56 | _Bool boolMember
+ 64 | int intMember
+ | [sizeof=72, dsize=68, align=8,
+ | nvsize=68, nvalign=8]
+
+*/
+class ClassWithClassMembers {
+ bool boolMember;
+ BasicClassLayout classMember;
+ ClassWithTwoVirtualBaseClasses virtualClassesMember;
+ double doubleMember;
+ ClassWithVirtualBase virtualClassMember;
+ int intMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class ClassWithPointerMember
+ 0 | _Bool boolMember
+ 8 | class BasicClassLayout * classMemberPointer
+ 16 | int intMember
+ | [sizeof=24, dsize=20, align=8,
+ | nvsize=20, nvalign=8]
+*/
+class ClassWithPointerMember {
+ bool boolMember;
+ BasicClassLayout* classMemberPointer;
+ int intMember;
+};
+
+/*
+*** Dumping AST Record Layout
+ 0 | class ClassWithBitfields
+ 0 | _Bool boolMember
+ 1:0-0 | unsigned int bitfield1
+ 1:1-2 | unsigned int bitfield2
+ 1:3-3 | unsigned int bitfield3
+ 1:4-4 | _Bool bitfield4
+ 1:5-6 | _Bool bitfield5
+ 1:7-7 | _Bool bitfield6
+ 4 | int intMember
+ 8:0-0 | unsigned int bitfield7
+ 8:1-1 | _Bool bitfield8
+ | [sizeof=12, dsize=9, align=4,
+ | nvsize=9, nvalign=4]
+*/
+class ClassWithBitfields {
+ bool boolMember;
+
+ unsigned bitfield1 : 1;
+ unsigned bitfield2 : 2;
+ unsigned bitfield3 : 1;
+ bool bitfield4 : 1;
+ bool bitfield5 : 2;
+ bool bitfield6 : 1;
+
+ int intMember;
+
+ unsigned bitfield7 : 1;
+ bool bitfield8 : 1;
+};
+
+void avoidClassDeadStripping()
+{
+ BasicClassLayout basicClassInstance;
+ BoolPaddingClass boolPaddingClassInstance;
+ ClassWithEmptyClassMembers classWithEmptyClassMembersInstance;
+ PaddingBetweenClassMembers paddingBetweenClassMembersInstance;
+ SimpleVirtualClass simpleVirtualClassInstance;
+ InterleavedVirtualNonVirtual interleavedVirtualNonVirtualInstance;
+ VirtualClassWithNonVirtualBase virtualClassWithNonVirtualBaseInstance;
+ ClassWithVirtualBase classWithVirtualBaseInstance;
+ ClassWithVirtualInheritance classWithVirtualInheritanceInstance;
+ DerivedClassWithIndirectVirtualInheritance derivedClassWithIndirectVirtualInheritanceInstance;
+ ClassWithInheritanceAndClassMember classWithInheritanceAndClassMemberInstance;
+ ClassWithClassMembers classWithClassMembersInstance;
+ ClassWithPointerMember classWithPointerMemberInstance;
+ ClassWithBitfields classWithBitfieldsInstance;
+}
Copied: trunk/Tools/lldb/lldbWebKitTester/DumpClassLayoutTesting.h (from rev 233613, trunk/Tools/lldb/lldbWebKitTester/main.cpp) (0 => 233614)
--- trunk/Tools/lldb/lldbWebKitTester/DumpClassLayoutTesting.h (rev 0)
+++ trunk/Tools/lldb/lldbWebKitTester/DumpClassLayoutTesting.h 2018-07-07 15:52:49 UTC (rev 233614)
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+void avoidClassDeadStripping();
Modified: trunk/Tools/lldb/lldbWebKitTester/lldbWebKitTester.xcodeproj/project.pbxproj (233613 => 233614)
--- trunk/Tools/lldb/lldbWebKitTester/lldbWebKitTester.xcodeproj/project.pbxproj 2018-07-07 07:10:20 UTC (rev 233613)
+++ trunk/Tools/lldb/lldbWebKitTester/lldbWebKitTester.xcodeproj/project.pbxproj 2018-07-07 15:52:49 UTC (rev 233614)
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 0FC1C35420E5391E001E9FB0 /* DumpClassLayoutTesting.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC1C35320E5391E001E9FB0 /* DumpClassLayoutTesting.cpp */; };
7CB6844B1AFA7978002B305C /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7CB6844A1AFA7978002B305C /* main.cpp */; };
CE6C3AFF20C0B17B003E33D8 /* libWTF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE6C3AFE20C0B17B003E33D8 /* libWTF.a */; };
CE6C3B0120C0C444003E33D8 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CE6C3B0020C0C443003E33D8 /* libicucore.dylib */; };
@@ -25,6 +26,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 0FC1C35320E5391E001E9FB0 /* DumpClassLayoutTesting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DumpClassLayoutTesting.cpp; sourceTree = "<group>"; };
+ 0FC1C35520E53932001E9FB0 /* DumpClassLayoutTesting.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DumpClassLayoutTesting.h; sourceTree = "<group>"; };
7C2227511AFC4D9C008C3338 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = "<group>"; };
7C2227521AFC4D9C008C3338 /* lldbWebKitTester.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = lldbWebKitTester.xcconfig; sourceTree = "<group>"; };
7C2227531AFC4D9C008C3338 /* DebugRelease.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DebugRelease.xcconfig; sourceTree = "<group>"; };
@@ -78,6 +81,8 @@
CE3F4424205C66D9007195B3 /* lldbWebKitTester */ = {
isa = PBXGroup;
children = (
+ 0FC1C35320E5391E001E9FB0 /* DumpClassLayoutTesting.cpp */,
+ 0FC1C35520E53932001E9FB0 /* DumpClassLayoutTesting.h */,
7CB6844A1AFA7978002B305C /* main.cpp */,
);
name = lldbWebKitTester;
@@ -142,6 +147,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 0FC1C35420E5391E001E9FB0 /* DumpClassLayoutTesting.cpp in Sources */,
7CB6844B1AFA7978002B305C /* main.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Modified: trunk/Tools/lldb/lldbWebKitTester/main.cpp (233613 => 233614)
--- trunk/Tools/lldb/lldbWebKitTester/main.cpp 2018-07-07 07:10:20 UTC (rev 233613)
+++ trunk/Tools/lldb/lldbWebKitTester/main.cpp 2018-07-07 15:52:49 UTC (rev 233614)
@@ -23,6 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "DumpClassLayoutTesting.h"
#include <stdio.h>
#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
@@ -65,6 +66,7 @@
int main(int argc, const char* argv[])
{
+ avoidClassDeadStripping();
testSummaryProviders();
fprintf(stderr, "This executable does nothing and is only meant for debugging lldb_webkit.py.\n");
return 0;
Added: trunk/Tools/lldb/lldb_dump_class_layout.py (0 => 233614)
--- trunk/Tools/lldb/lldb_dump_class_layout.py (rev 0)
+++ trunk/Tools/lldb/lldb_dump_class_layout.py 2018-07-07 15:52:49 UTC (rev 233614)
@@ -0,0 +1,391 @@
+#!/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 os
+import re
+import subprocess
+import sys
+
+from webkitpy.common.system.systemhost import SystemHost
+sys.path.append(SystemHost().path_to_lldb_python_directory())
+import lldb
+
+# lldb Python reference:
+# <https://lldb.llvm.org/python_reference/>
+
+
+class AnsiColors:
+ BLUE = '\033[94m'
+ WARNING = '\033[93m'
+ ENDCOLOR = '\033[0m'
+
+
+class ClassLayoutBase(object):
+
+ MEMBER_NAME_KEY = 'name'
+ MEMBER_TYPE_KEY = 'type'
+ MEMBER_TYPE_CLASS = 'type_class' # lldb.eTypeClassStruct etc. Values here: <https://lldb.llvm.org/python_reference/_lldb%27-module.html#eTypeClassAny>
+ MEMBER_CLASS_INSTANCE = 'class_instance'
+ MEMBER_BYTE_SIZE = 'byte_size'
+ MEMBER_OFFSET = 'offset' # offset is local to this class.
+ MEMBER_IS_BITFIELD = 'is_bitfield'
+ MEMBER_BITFIELD_BIT_SIZE = 'bit_size'
+ PADDING_TYPE = '<PADDING>'
+ PADDING_NAME = ''
+
+ def __init__(self, typename):
+ self.typename = typename
+ self.total_byte_size = 0
+ self.total_pad_bytes = 0
+ self.data_members = []
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ return False
+
+ if self.total_byte_size != other.total_byte_size:
+ return False
+
+ if self.total_pad_bytes != other.total_pad_bytes:
+ return False
+
+ if len(self.data_members) != other.data_members:
+ return False
+
+ for i in range(len(self.data_members)):
+ self_member = self.data_members[i]
+ other_member = other.data_members[i]
+ if self_member != other_member:
+ return False
+
+ return True
+
+ def _to_string_recursive(self, str_list, colorize, member_name=None, depth=0, total_offset=0):
+ type_start = AnsiColors.BLUE if colorize else ''
+ warn_start = AnsiColors.WARNING if colorize else ''
+ color_end = AnsiColors.ENDCOLOR if colorize else ''
+
+ if member_name:
+ str_list.append('%+4u <%3u> %s%s%s%s %s' % (total_offset, self.total_byte_size, ' ' * depth, type_start, self.typename, color_end, member_name))
+ else:
+ str_list.append('%+4u <%3u> %s%s%s%s' % (total_offset, self.total_byte_size, ' ' * depth, type_start, self.typename, color_end))
+
+ start_offset = total_offset
+
+ for data_member in self.data_members:
+ member_total_offset = start_offset + data_member[self.MEMBER_OFFSET]
+ if self.MEMBER_CLASS_INSTANCE in data_member:
+ data_member[self.MEMBER_CLASS_INSTANCE]._to_string_recursive(str_list, colorize, data_member[self.MEMBER_NAME_KEY], depth + 1, member_total_offset)
+ else:
+ byte_size = data_member[self.MEMBER_BYTE_SIZE]
+
+ if self.MEMBER_IS_BITFIELD in data_member:
+ str_list.append('%+4u <%3u> %s %s %s : %d' % (member_total_offset, byte_size, ' ' * depth, data_member[self.MEMBER_TYPE_KEY], data_member[self.MEMBER_NAME_KEY], data_member[self.MEMBER_BITFIELD_BIT_SIZE]))
+ elif data_member[self.MEMBER_TYPE_KEY] == self.PADDING_TYPE:
+ str_list.append('%+4u <%3u> %s %s<PADDING: %d %s>%s' % (member_total_offset, byte_size, ' ' * depth, warn_start, byte_size, 'bytes' if byte_size > 1 else 'byte', color_end))
+ else:
+ str_list.append('%+4u <%3u> %s %s %s' % (member_total_offset, byte_size, ' ' * depth, data_member[self.MEMBER_TYPE_KEY], data_member[self.MEMBER_NAME_KEY]))
+
+ def as_string_list(self, colorize=False):
+ str_list = []
+ self._to_string_recursive(str_list, colorize)
+ str_list.append('Total byte size: %d' % (self.total_byte_size))
+ str_list.append('Total pad bytes: %d' % (self.total_pad_bytes))
+ if self.total_pad_bytes > 0:
+ str_list.append('Padding percentage: %2.2f %%' % ((float(self.total_pad_bytes) / float(self.total_byte_size)) * 100.0))
+ return str_list
+
+ def as_string(self, colorize=False):
+ return '\n'.join(self.as_string_list(colorize))
+
+ def dump(self, colorize=True):
+ print self.as_string(colorize)
+
+
+class ClassLayout(ClassLayoutBase):
+ "Stores the layout of a class or struct."
+
+ def __init__(self, target, type, containerClass=None, derivedClass=None):
+ super(ClassLayout, self).__init__(type.GetName())
+
+ self.target = target
+ self.type = type
+ self.total_byte_size = self.type.GetByteSize()
+ self.pointer_size = self.target.GetAddressByteSize()
+ self.total_pad_bytes = 0
+ self.data_members = []
+ self.virtual_base_classes = self._virtual_base_classes_dictionary()
+ self._parse(containerClass, derivedClass)
+ if containerClass == None and derivedClass == None:
+ self.total_pad_bytes = self._compute_padding()
+
+ def _has_polymorphic_non_virtual_base_class(self):
+ num_base_classes = self.type.GetNumberOfDirectBaseClasses()
+ for i in range(num_base_classes):
+ base_class = self.type.GetDirectBaseClassAtIndex(i)
+ if base_class.GetName() in self.virtual_base_classes:
+ continue
+
+ if base_class.GetType().IsPolymorphicClass():
+ return True
+
+ return False
+
+ def _virtual_base_classes_dictionary(self):
+ result = {}
+ num_virtual_base_classes = self.type.GetNumberOfVirtualBaseClasses()
+ for i in range(num_virtual_base_classes):
+ virtual_base = self.type.GetVirtualBaseClassAtIndex(i)
+ result[virtual_base.GetName()] = ClassLayout(self.target, virtual_base.GetType(), self)
+ return result
+
+ def _parse(self, containerClass=None, derivedClass=None):
+ # It's moot where we actually show the vtable pointer, but to match clang -fdump-record-layouts, assign it to the
+ # base-most polymorphic class (unless virtual inheritance is involved).
+ if self.type.IsPolymorphicClass() and not self._has_polymorphic_non_virtual_base_class():
+ data_member = {
+ self.MEMBER_NAME_KEY : '__vtbl_ptr_type * _vptr',
+ self.MEMBER_TYPE_KEY : '',
+ self.MEMBER_BYTE_SIZE : self.pointer_size,
+ self.MEMBER_OFFSET : 0
+ }
+ self.data_members.append(data_member)
+
+ num_direct_base_classes = self.type.GetNumberOfDirectBaseClasses()
+ if num_direct_base_classes > 0:
+ for i in range(num_direct_base_classes):
+ direct_base = self.type.GetDirectBaseClassAtIndex(i)
+
+ # virtual base classes are also considered direct base classes, but we need to skip those here.
+ if direct_base.GetName() in self.virtual_base_classes:
+ continue
+
+ member_type = direct_base.GetType()
+ member_typename = member_type.GetName()
+ member_canonical_type = member_type.GetCanonicalType()
+ member_type_class = member_canonical_type.GetTypeClass()
+
+ member_name = direct_base.GetName()
+ member_offset = direct_base.GetOffsetInBytes()
+ member_byte_size = member_type.GetByteSize()
+
+ base_class = ClassLayout(self.target, member_type, None, self)
+
+ data_member = {
+ self.MEMBER_NAME_KEY : member_name,
+ self.MEMBER_TYPE_KEY : member_typename,
+ self.MEMBER_TYPE_CLASS : member_type_class,
+ self.MEMBER_CLASS_INSTANCE : base_class,
+ self.MEMBER_BYTE_SIZE : member_byte_size,
+ self.MEMBER_OFFSET : member_offset,
+ }
+
+ self.data_members.append(data_member)
+
+ num_fields = self.type.GetNumberOfFields()
+ for i in range(num_fields):
+ field = self.type.GetFieldAtIndex(i)
+
+ member_type = field.GetType()
+ member_typename = member_type.GetName()
+ member_canonical_type = member_type.GetCanonicalType()
+ member_type_class = member_canonical_type.GetTypeClass()
+
+ member_name = field.GetName()
+ member_offset = field.GetOffsetInBytes()
+ member_byte_size = member_type.GetByteSize()
+
+ data_member = {
+ self.MEMBER_NAME_KEY : member_name,
+ self.MEMBER_TYPE_KEY : member_typename,
+ self.MEMBER_TYPE_CLASS : member_type_class,
+ self.MEMBER_BYTE_SIZE : member_byte_size,
+ self.MEMBER_OFFSET : member_offset,
+ }
+
+ if field.IsBitfield():
+ data_member[self.MEMBER_IS_BITFIELD] = True
+ data_member[self.MEMBER_BITFIELD_BIT_SIZE] = field.GetBitfieldSizeInBits()
+ elif member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass:
+ nested_class = ClassLayout(self.target, member_type, self)
+ data_member[self.MEMBER_CLASS_INSTANCE] = nested_class
+
+ self.data_members.append(data_member)
+
+ # "For each distinct base class that is specified virtual, the most derived object contains only one base class subobject of that type,
+ # even if the class appears many times in the inheritance hierarchy (as long as it is inherited virtual every time)."
+ num_virtual_base_classes = self.type.GetNumberOfVirtualBaseClasses()
+ if derivedClass == None and num_virtual_base_classes > 0:
+ for i in range(num_virtual_base_classes):
+ virtual_base = self.type.GetVirtualBaseClassAtIndex(i)
+ member_type = virtual_base.GetType()
+ member_typename = member_type.GetName()
+ member_canonical_type = member_type.GetCanonicalType()
+ member_type_class = member_canonical_type.GetTypeClass()
+
+ member_name = virtual_base.GetName()
+ member_offset = virtual_base.GetOffsetInBytes()
+ member_byte_size = member_type.GetByteSize()
+
+ nested_class = ClassLayout(self.target, member_type, None, self)
+
+ data_member = {
+ self.MEMBER_NAME_KEY : member_name,
+ self.MEMBER_TYPE_KEY : member_typename,
+ self.MEMBER_TYPE_CLASS : member_type_class,
+ self.MEMBER_CLASS_INSTANCE : nested_class,
+ self.MEMBER_BYTE_SIZE : member_byte_size,
+ self.MEMBER_OFFSET : member_offset,
+ }
+
+ self.data_members.append(data_member)
+
+ # clang -fdump-record-layouts shows "(empty)" for such classes, but I can't find any way to access this information via lldb.
+ def _probably_has_empty_base_class_optimization(self):
+ if self.total_byte_size > 1:
+ return False
+
+ if len(self.data_members) > 1:
+ return False
+
+ if len(self.data_members) == 1:
+ data_member = self.data_members[0]
+ if self.MEMBER_CLASS_INSTANCE in data_member:
+ return data_member[self.MEMBER_CLASS_INSTANCE]._probably_has_empty_base_class_optimization()
+
+ return True
+
+ def _compute_padding_recursive(self, total_offset=0, depth=0, containerClass=None):
+ padding_bytes = 0
+ start_offset = total_offset
+ current_offset = total_offset
+
+ i = 0
+ while i < len(self.data_members):
+ data_member = self.data_members[i]
+ member_offset = data_member[self.MEMBER_OFFSET]
+
+ probably_empty_base_class = False
+ if self.MEMBER_CLASS_INSTANCE in data_member:
+ probably_empty_base_class = member_offset == 0 and data_member[self.MEMBER_CLASS_INSTANCE]._probably_has_empty_base_class_optimization()
+
+ byte_size = data_member[self.MEMBER_BYTE_SIZE]
+
+ if not probably_empty_base_class:
+ padding_size = start_offset + member_offset - current_offset
+
+ if padding_size > 0:
+ padding_member = {
+ self.MEMBER_NAME_KEY : self.PADDING_NAME,
+ self.MEMBER_TYPE_KEY : self.PADDING_TYPE,
+ self.MEMBER_BYTE_SIZE : padding_size,
+ self.MEMBER_OFFSET : current_offset,
+ }
+
+ self.data_members.insert(i, padding_member)
+ padding_bytes += padding_size
+ i += 1
+
+ current_offset = start_offset + member_offset
+
+ if self.MEMBER_CLASS_INSTANCE in data_member:
+ [padding, offset] = data_member[self.MEMBER_CLASS_INSTANCE]._compute_padding_recursive(current_offset, depth + 1, self)
+ padding_bytes += padding
+ current_offset = offset
+ else:
+ current_offset += byte_size
+
+ i += 1
+
+ # Look for padding at the end.
+ if containerClass == None:
+ padding_size = self.total_byte_size - current_offset
+ if padding_size > 0:
+ padding_member = {
+ self.MEMBER_NAME_KEY : self.PADDING_NAME,
+ self.MEMBER_TYPE_KEY : self.PADDING_TYPE,
+ self.MEMBER_BYTE_SIZE : padding_size,
+ self.MEMBER_OFFSET : current_offset,
+ }
+ self.data_members.append(padding_member)
+ padding_bytes += padding_size
+
+ return [padding_bytes, current_offset]
+
+ def _compute_padding(self):
+ [padding, offset] = self._compute_padding_recursive()
+ return padding
+
+
+class LLDBDebuggerInstance:
+ "Wraps an instance of lldb.SBDebugger and vends ClassLayouts"
+
+ def __init__(self, binary_path, architecture):
+ self.binary_path = binary_path
+ self.architecture = architecture
+
+ self.debugger = lldb.SBDebugger.Create()
+ self.debugger.SetAsync(False)
+ architecture = self.architecture
+ if not architecture:
+ architecture = self._get_first_file_architecture()
+
+ self.target = self.debugger.CreateTargetWithFileAndArch(str(self.binary_path), architecture)
+ if not self.target:
+ print "Failed to make target for " + self.binary_path
+
+ self.module = self.target.GetModuleAtIndex(0)
+ if not self.module:
+ print "Failed to get first module in " + self.binary_path
+
+ def __del__(self):
+ if lldb:
+ lldb.SBDebugger.Destroy(self.debugger)
+
+ def _get_first_file_architecture(self):
+ p = re.compile('shared library +(\w+)$')
+ file_result = subprocess.check_output(["file", self.binary_path]).split('\n')
+ arches = []
+ for line in file_result:
+ match = p.search(line)
+ if match:
+ arches.append(match.group(1))
+
+ if len(arches) > 0:
+ return arches[0]
+
+ return lldb.LLDB_ARCH_DEFAULT
+
+ def layout_for_classname(self, classname):
+ types = self.module.FindTypes(classname)
+ if types.GetSize():
+ # There can be more that one type with a given name, but for now just return the first one.
+ return ClassLayout(self.target, types.GetTypeAtIndex(0))
+
+ print 'error: no type matches "%s" in "%s"' % (classname, self.module.file)
+ return None
Property changes on: trunk/Tools/lldb/lldb_dump_class_layout.py
___________________________________________________________________
Added: svn:executable
+*
\ No newline at end of property