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 re 16import signal 17import sys 18 19try: 20 # Just try for LLDB in case PYTHONPATH is already correctly setup 21 import lldb 22except ImportError: 23 lldb_python_dirs = list() 24 # lldb is not in the PYTHONPATH, try some defaults for the current platform 25 platform_system = platform.system() 26 if platform_system == 'Darwin': 27 # On Darwin, try the currently selected Xcode directory 28 xcode_dir = commands.getoutput("xcode-select --print-path") 29 if xcode_dir: 30 lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) 31 lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 32 lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 33 success = False 34 for lldb_python_dir in lldb_python_dirs: 35 if os.path.exists(lldb_python_dir): 36 if not (sys.path.__contains__(lldb_python_dir)): 37 sys.path.append(lldb_python_dir) 38 try: 39 import lldb 40 except ImportError: 41 pass 42 else: 43 print 'imported lldb from: "%s"' % (lldb_python_dir) 44 success = True 45 break 46 if not success: 47 print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" 48 sys.exit(1) 49 50import commands 51import optparse 52import shlex 53import time 54 55def regex_option_callback(option, opt_str, value, parser): 56 if opt_str == "--std": 57 value = '^std::' 58 regex = re.compile(value) 59 parser.values.skip_type_regexes.append (regex) 60 61def create_types_options(for_lldb_command): 62 if for_lldb_command: 63 usage = "usage: %prog [options]" 64 description='''This command will help check for padding in between 65base classes and members in structs and classes. It will summarize the types 66and how much padding was found. If no types are specified with the --types TYPENAME 67option, all structure and class types will be verified. If no modules are 68specified with the --module option, only the target's main executable will be 69searched. 70''' 71 else: 72 usage = "usage: %prog [options] EXEPATH [EXEPATH ...]" 73 description='''This command will help check for padding in between 74base classes and members in structures and classes. It will summarize the types 75and how much padding was found. One or more paths to executable files must be 76specified and targets will be created with these modules. If no types are 77specified with the --types TYPENAME option, all structure and class types will 78be verified in all specified modules. 79''' 80 parser = optparse.OptionParser(description=description, prog='framestats',usage=usage) 81 if not for_lldb_command: 82 parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) 83 parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') 84 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=[]) 85 parser.add_option('-d', '--debug', action='store_true', dest='debug', help='Pause 10 seconds to wait for a debugger to attach.', default=False) 86 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=[]) 87 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='Enable verbose logging and information.', default=False) 88 parser.add_option('-s', '--skip-type-regex', action="callback", callback=regex_option_callback, type='string', metavar='REGEX', dest='skip_type_regexes', help='Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.', default=[]) 89 parser.add_option('--std', action="callback", callback=regex_option_callback, metavar='REGEX', dest='skip_type_regexes', help="Don't' recurse into types in the std namespace.", default=[]) 90 return parser 91 92def verify_type (target, options, type): 93 print type 94 typename = type.GetName() 95 # print 'type: %s' % (typename) 96 (end_offset, padding) = verify_type_recursive (target, options, type, None, 0, 0, 0) 97 byte_size = type.GetByteSize() 98 # if end_offset < byte_size: 99 # last_member_padding = byte_size - end_offset 100 # print '%+4u <%u> padding' % (end_offset, last_member_padding) 101 # padding += last_member_padding 102 print 'Total byte size: %u' % (byte_size) 103 print 'Total pad bytes: %u' % (padding) 104 if padding > 0: 105 print 'Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0) 106 print 107 108def verify_type_recursive (target, options, type, member_name, depth, base_offset, padding): 109 prev_end_offset = base_offset 110 typename = type.GetName() 111 byte_size = type.GetByteSize() 112 if member_name and member_name != typename: 113 print '%+4u <%3u> %s%s %s;' % (base_offset, byte_size, ' ' * depth, typename, member_name) 114 else: 115 print '%+4u {%3u} %s%s' % (base_offset, byte_size, ' ' * depth, typename) 116 117 for type_regex in options.skip_type_regexes: 118 match = type_regex.match (typename) 119 if match: 120 return (base_offset + byte_size, padding) 121 122 members = type.members 123 if members: 124 for member_idx, member in enumerate(members): 125 member_type = member.GetType() 126 member_canonical_type = member_type.GetCanonicalType() 127 member_type_class = member_canonical_type.GetTypeClass() 128 member_name = member.GetName() 129 member_offset = member.GetOffsetInBytes() 130 member_total_offset = member_offset + base_offset 131 member_byte_size = member_type.GetByteSize() 132 member_is_class_or_struct = False 133 if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass: 134 member_is_class_or_struct = True 135 if member_idx == 0 and member_offset == target.GetAddressByteSize() and type.IsPolymorphicClass(): 136 ptr_size = target.GetAddressByteSize() 137 print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1)) 138 prev_end_offset = ptr_size 139 else: 140 if prev_end_offset < member_total_offset: 141 member_padding = member_total_offset - prev_end_offset 142 padding = padding + member_padding 143 print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1)) 144 145 if member_is_class_or_struct: 146 (prev_end_offset, padding) = verify_type_recursive (target, options, member_canonical_type, member_name, depth + 1, member_total_offset, padding) 147 else: 148 prev_end_offset = member_total_offset + member_byte_size 149 member_typename = member_type.GetName() 150 if member.IsBitfield(): 151 print '%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member.GetBitfieldSizeInBits(), member_name) 152 else: 153 print '%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member_name) 154 155 if prev_end_offset < byte_size: 156 last_member_padding = byte_size - prev_end_offset 157 print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, last_member_padding, ' ' * (depth + 1)) 158 padding += last_member_padding 159 else: 160 if type.IsPolymorphicClass(): 161 ptr_size = target.GetAddressByteSize() 162 print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1)) 163 prev_end_offset = ptr_size 164 prev_end_offset = base_offset + byte_size 165 166 return (prev_end_offset, padding) 167 168def check_padding_command (debugger, command, result, dict): 169 # Use the Shell Lexer to properly parse up command options just like a 170 # shell would 171 command_args = shlex.split(command) 172 parser = create_types_options(True) 173 try: 174 (options, args) = parser.parse_args(command_args) 175 except: 176 # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit 177 # (courtesy of OptParse dealing with argument errors by throwing SystemExit) 178 result.SetStatus (lldb.eReturnStatusFailed) 179 return "option parsing failed" # returning a string is the same as returning an error whose description is the string 180 verify_types(options, debugger.GetSelectedTarget(), command_args) 181 182@lldb.command("parse_all_struct_class_types") 183def parse_all_struct_class_types (debugger, command, result, dict): 184 command_args = shlex.split(command) 185 for f in command_args: 186 error = lldb.SBError() 187 target = debugger.CreateTarget (f, None, None, False, error) 188 module = target.GetModuleAtIndex(0) 189 print "Parsing all types in '%s'" % (module) 190 types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) 191 for t in types: 192 print t 193 print "" 194 195 196def verify_types (target, options): 197 198 if not target: 199 print 'error: invalid target' 200 return 201 202 modules = list() 203 if len(options.modules) == 0: 204 # Append just the main executable if nothing was specified 205 module = target.modules[0] 206 if module: 207 modules.append(module) 208 else: 209 for module_name in options.modules: 210 module = lldb.target.module[module_name] 211 if module: 212 modules.append(module) 213 214 if modules: 215 for module in modules: 216 print 'module: %s' % (module.file) 217 if options.typenames: 218 for typename in options.typenames: 219 types = module.FindTypes(typename) 220 if types.GetSize(): 221 print 'Found %u types matching "%s" in "%s"' % (len(types), typename, module.file) 222 for type in types: 223 verify_type (target, options, type) 224 else: 225 print 'error: no type matches "%s" in "%s"' % (typename, module.file) 226 else: 227 types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) 228 print 'Found %u types in "%s"' % (len(types), module.file) 229 for type in types: 230 verify_type (target, options, type) 231 else: 232 print 'error: no modules' 233 234if __name__ == '__main__': 235 debugger = lldb.SBDebugger.Create() 236 parser = create_types_options(False) 237 238 # try: 239 (options, args) = parser.parse_args(sys.argv[1:]) 240 # except: 241 # print "error: option parsing failed" 242 # sys.exit(1) 243 244 if options.debug: 245 print "Waiting for debugger to attach to process %d" % os.getpid() 246 os.kill(os.getpid(), signal.SIGSTOP) 247 248 for path in args: 249 # in a command - the lldb.* convenience variables are not to be used 250 # and their values (if any) are undefined 251 # this is the best practice to access those objects from within a command 252 error = lldb.SBError() 253 target = debugger.CreateTarget (path, 254 options.arch, 255 options.platform, 256 True, 257 error) 258 if error.Fail(): 259 print error.GetCString() 260 continue 261 verify_types (target, options) 262 263elif getattr(lldb, 'debugger', None): 264 lldb.debugger.HandleCommand('command script add -f types.check_padding_command check_padding') 265 print '"check_padding" command installed, use the "--help" option for detailed help'