1#!/usr/bin/python 2 3#---------------------------------------------------------------------- 4# Be sure to add the python path that points to the LLDB shared library. 5# 6# # To use this in the embedded python interpreter using "lldb" just 7# import it with the full path using the "command script import" 8# command 9# (lldb) command script import /path/to/cmdtemplate.py 10#---------------------------------------------------------------------- 11 12import commands 13import platform 14import os 15import sys 16 17try: 18 # Just try for LLDB in case PYTHONPATH is already correctly setup 19 import lldb 20except ImportError: 21 lldb_python_dirs = list() 22 # lldb is not in the PYTHONPATH, try some defaults for the current platform 23 platform_system = platform.system() 24 if platform_system == 'Darwin': 25 # On Darwin, try the currently selected Xcode directory 26 xcode_dir = commands.getoutput("xcode-select --print-path") 27 if xcode_dir: 28 lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) 29 lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 30 lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 31 success = False 32 for lldb_python_dir in lldb_python_dirs: 33 if os.path.exists(lldb_python_dir): 34 if not (sys.path.__contains__(lldb_python_dir)): 35 sys.path.append(lldb_python_dir) 36 try: 37 import lldb 38 except ImportError: 39 pass 40 else: 41 print 'imported lldb from: "%s"' % (lldb_python_dir) 42 success = True 43 break 44 if not success: 45 print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" 46 sys.exit(1) 47 48import commands 49import optparse 50import shlex 51import time 52 53def create_types_options(): 54 usage = "usage: %prog [options]" 55 description='''This command will help you verify that types in your program 56are packed efficiently by showing all types and their sizes and showing the 57padding bytes that waste space. 58''' 59 parser = optparse.OptionParser(description=description, prog='framestats',usage=usage) 60 parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) 61 parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name') 62 parser.add_option('-m', '--module', action='append', type='string', metavar='MODULE', dest='modules', help='Specify one or more modules which will be used to verify the types.', default=[]) 63 parser.add_option('-t', '--type', action='append', type='string', metavar='TYPENAME', dest='typenames', help='Specify one or more type names which should be verified. If no type names are specified, all class and struct types will be verified.', default=[]) 64 parser.add_option('-d', '--debug', action='store_true', dest='debug', help='Pause 10 seconds to wait for a debugger to attach.', default=False) 65 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False) 66 return parser 67 68def verify_type (target, type): 69 print type 70 typename = type.GetName() 71 # print 'type: %s' % (typename) 72 (end_offset, padding) = verify_type_recursive (target, type, None, 0, 0, 0) 73 byte_size = type.GetByteSize() 74 # if end_offset < byte_size: 75 # last_member_padding = byte_size - end_offset 76 # print '%+4u <%u> padding' % (end_offset, last_member_padding) 77 # padding += last_member_padding 78 print 'Total byte size: %u' % (byte_size) 79 print 'Total pad bytes: %u' % (padding) 80 if padding > 0: 81 print 'Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0) 82 print 83 84def verify_type_recursive (target, type, member_name, depth, base_offset, padding): 85 prev_end_offset = base_offset 86 typename = type.GetName() 87 byte_size = type.GetByteSize() 88 if member_name and member_name != typename: 89 print '%+4u <%3u> %s%s %s;' % (base_offset, type.GetByteSize(), ' ' * depth, typename, member_name) 90 else: 91 print '%+4u <%3u> %s%s' % (base_offset, type.GetByteSize(), ' ' * depth, typename) 92 93 members = type.members 94 if members: 95 for member_idx, member in enumerate(members): 96 member_type = member.GetType() 97 member_canonical_type = member_type.GetCanonicalType() 98 member_type_class = member_canonical_type.GetTypeClass() 99 member_name = member.GetName() 100 member_offset = member.GetOffsetInBytes() 101 member_total_offset = member_offset + base_offset 102 member_byte_size = member_type.GetByteSize() 103 is_class_or_struct = False 104 if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass: 105 is_class_or_struct = True 106 if member_idx == 0 and member_offset == target.GetAddressByteSize() and type.IsPolymorphicClass(): 107 ptr_size = target.GetAddressByteSize() 108 print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1)) 109 prev_end_offset = ptr_size 110 else: 111 if prev_end_offset < member_total_offset: 112 member_padding = member_total_offset - prev_end_offset 113 padding = padding + member_padding 114 print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1)) 115 116 if is_class_or_struct: 117 (prev_end_offset, padding) = verify_type_recursive (target, member_canonical_type, member_name, depth + 1, member_total_offset, padding) 118 else: 119 prev_end_offset = member_total_offset + member_byte_size 120 if member.IsBitfield(): 121 print '%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_type.GetName(), member.GetBitfieldSizeInBits(), member_name) 122 else: 123 print '%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_type.GetName(), member_name) 124 125 if prev_end_offset < byte_size: 126 last_member_padding = byte_size - prev_end_offset 127 print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, last_member_padding, ' ' * (depth + 1)) 128 padding += last_member_padding 129 else: 130 if type.IsPolymorphicClass(): 131 ptr_size = target.GetAddressByteSize() 132 print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1)) 133 prev_end_offset = ptr_size 134 prev_end_offset = base_offset + type.GetByteSize() 135 136 return (prev_end_offset, padding) 137 138def types_command (debugger, command, result, dict): 139 # Use the Shell Lexer to properly parse up command options just like a 140 # shell would 141 command_args = shlex.split(command) 142 verify_types(debugger, command_args) 143 144def verify_types (debugger, command_args): 145 146 parser = create_types_options() 147 try: 148 (options, args) = parser.parse_args(command_args) 149 except: 150 # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit 151 # (courtesy of OptParse dealing with argument errors by throwing SystemExit) 152 result.SetStatus (lldb.eReturnStatusFailed) 153 return "option parsing failed" # returning a string is the same as returning an error whose description is the string 154 155 if options.debug: 156 print 'Waiting for debugger to attach...' 157 for i in range(10): 158 time.sleep(1) 159 print '.' 160 161 for path in args: 162 # in a command - the lldb.* convenience variables are not to be used 163 # and their values (if any) are undefined 164 # this is the best practice to access those objects from within a command 165 error = lldb.SBError() 166 target = debugger.CreateTarget (path, 167 options.arch, 168 options.platform, 169 True, 170 error) 171 if error.Fail(): 172 print error.GetCString() 173 continue 174 175 modules = list() 176 if len(options.modules) == 0: 177 # Append just the main executable if nothing was specified 178 module = target.modules[0] 179 if module: 180 modules.append(module) 181 else: 182 for module_name in options.modules: 183 module = lldb.target.module[module_name] 184 if module: 185 modules.append(module) 186 187 if modules: 188 for module in modules: 189 print 'module: %s' % (module.file) 190 if options.typenames: 191 for typename in options.typenames: 192 types = module.FindTypes(typename) 193 if types.GetSize(): 194 print 'Found %u types matching "%s" in "%s"' % (len(types), typename, module.file) 195 for type in types: 196 verify_type (target, type) 197 else: 198 print 'error: no type matches "%s" in "%s"' % (typename, module.file) 199 else: 200 types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) 201 print 'Found %u types in "%s"' % (len(types), module.file) 202 for type in types: 203 verify_type (target, type) 204 else: 205 print 'error: no modules' 206 207if __name__ == '__main__': 208 debugger = lldb.SBDebugger.Create() 209 verify_types (debugger, sys.argv[1:]) 210elif getattr(lldb, 'debugger', None): 211 lldb.debugger.HandleCommand('command script add -f types.types_command types') 212 print '"types" command installed, use the "--help" option for detailed help'