1515bc8c1Sserge-sans-paille#!/usr/bin/env python 2d8c3d4b1SGreg Clayton 3d8c3d4b1SGreg Clayton#---------------------------------------------------------------------- 4d8c3d4b1SGreg Clayton# Be sure to add the python path that points to the LLDB shared library. 5d8c3d4b1SGreg Clayton# 6d8c3d4b1SGreg Clayton# # To use this in the embedded python interpreter using "lldb" just 7d8c3d4b1SGreg Clayton# import it with the full path using the "command script import" 8d8c3d4b1SGreg Clayton# command 9d8c3d4b1SGreg Clayton# (lldb) command script import /path/to/cmdtemplate.py 10d8c3d4b1SGreg Clayton#---------------------------------------------------------------------- 11d8c3d4b1SGreg Clayton 12525cd59fSSerge Gueltonfrom __future__ import print_function 13525cd59fSSerge Guelton 14d8c3d4b1SGreg Claytonimport platform 15d8c3d4b1SGreg Claytonimport os 1657d1c486SGreg Claytonimport re 17e110ad8fSGreg Claytonimport signal 18d8c3d4b1SGreg Claytonimport sys 191a12dd70SSerge Gueltonimport subprocess 201a12dd70SSerge Guelton 21d8c3d4b1SGreg Claytontry: 22d8c3d4b1SGreg Clayton # Just try for LLDB in case PYTHONPATH is already correctly setup 23d8c3d4b1SGreg Clayton import lldb 24d8c3d4b1SGreg Claytonexcept ImportError: 25d8c3d4b1SGreg Clayton lldb_python_dirs = list() 26d8c3d4b1SGreg Clayton # lldb is not in the PYTHONPATH, try some defaults for the current platform 27d8c3d4b1SGreg Clayton platform_system = platform.system() 28d8c3d4b1SGreg Clayton if platform_system == 'Darwin': 29d8c3d4b1SGreg Clayton # On Darwin, try the currently selected Xcode directory 30185de8eeSDavide Italiano xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True) 31d8c3d4b1SGreg Clayton if xcode_dir: 32b9c1b51eSKate Stone lldb_python_dirs.append( 33b9c1b51eSKate Stone os.path.realpath( 34b9c1b51eSKate Stone xcode_dir + 35b9c1b51eSKate Stone '/../SharedFrameworks/LLDB.framework/Resources/Python')) 36b9c1b51eSKate Stone lldb_python_dirs.append( 37b9c1b51eSKate Stone xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 38b9c1b51eSKate Stone lldb_python_dirs.append( 39b9c1b51eSKate Stone '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') 40d8c3d4b1SGreg Clayton success = False 41d8c3d4b1SGreg Clayton for lldb_python_dir in lldb_python_dirs: 42d8c3d4b1SGreg Clayton if os.path.exists(lldb_python_dir): 43d8c3d4b1SGreg Clayton if not (sys.path.__contains__(lldb_python_dir)): 44d8c3d4b1SGreg Clayton sys.path.append(lldb_python_dir) 45d8c3d4b1SGreg Clayton try: 46d8c3d4b1SGreg Clayton import lldb 47d8c3d4b1SGreg Clayton except ImportError: 48d8c3d4b1SGreg Clayton pass 49d8c3d4b1SGreg Clayton else: 50525cd59fSSerge Guelton print('imported lldb from: "%s"' % (lldb_python_dir)) 51d8c3d4b1SGreg Clayton success = True 52d8c3d4b1SGreg Clayton break 53d8c3d4b1SGreg Clayton if not success: 54525cd59fSSerge Guelton print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly") 55d8c3d4b1SGreg Clayton sys.exit(1) 56d8c3d4b1SGreg Clayton 57d8c3d4b1SGreg Claytonimport optparse 58d8c3d4b1SGreg Claytonimport shlex 59d8c3d4b1SGreg Claytonimport time 60d8c3d4b1SGreg Clayton 61b9c1b51eSKate Stone 6257d1c486SGreg Claytondef regex_option_callback(option, opt_str, value, parser): 6357d1c486SGreg Clayton if opt_str == "--std": 6457d1c486SGreg Clayton value = '^std::' 6557d1c486SGreg Clayton regex = re.compile(value) 6657d1c486SGreg Clayton parser.values.skip_type_regexes.append(regex) 6757d1c486SGreg Clayton 68b9c1b51eSKate Stone 6957d1c486SGreg Claytondef create_types_options(for_lldb_command): 7057d1c486SGreg Clayton if for_lldb_command: 71d8c3d4b1SGreg Clayton usage = "usage: %prog [options]" 7257d1c486SGreg Clayton description = '''This command will help check for padding in between 7357d1c486SGreg Claytonbase classes and members in structs and classes. It will summarize the types 7457d1c486SGreg Claytonand how much padding was found. If no types are specified with the --types TYPENAME 7557d1c486SGreg Claytonoption, all structure and class types will be verified. If no modules are 7657d1c486SGreg Claytonspecified with the --module option, only the target's main executable will be 7757d1c486SGreg Claytonsearched. 7857d1c486SGreg Clayton''' 7957d1c486SGreg Clayton else: 8057d1c486SGreg Clayton usage = "usage: %prog [options] EXEPATH [EXEPATH ...]" 8157d1c486SGreg Clayton description = '''This command will help check for padding in between 8257d1c486SGreg Claytonbase classes and members in structures and classes. It will summarize the types 8357d1c486SGreg Claytonand how much padding was found. One or more paths to executable files must be 8457d1c486SGreg Claytonspecified and targets will be created with these modules. If no types are 8557d1c486SGreg Claytonspecified with the --types TYPENAME option, all structure and class types will 8657d1c486SGreg Claytonbe verified in all specified modules. 87d8c3d4b1SGreg Clayton''' 88b9c1b51eSKate Stone parser = optparse.OptionParser( 89b9c1b51eSKate Stone description=description, 90b9c1b51eSKate Stone prog='framestats', 91b9c1b51eSKate Stone usage=usage) 9257d1c486SGreg Clayton if not for_lldb_command: 93b9c1b51eSKate Stone parser.add_option( 94b9c1b51eSKate Stone '-a', 95b9c1b51eSKate Stone '--arch', 96b9c1b51eSKate Stone type='string', 97b9c1b51eSKate Stone dest='arch', 98b9c1b51eSKate Stone help='The architecture to use when creating the debug target.', 99b9c1b51eSKate Stone default=None) 100b9c1b51eSKate Stone parser.add_option( 101b9c1b51eSKate Stone '-p', 102b9c1b51eSKate Stone '--platform', 103b9c1b51eSKate Stone type='string', 104b9c1b51eSKate Stone metavar='platform', 105b9c1b51eSKate Stone dest='platform', 106b9c1b51eSKate Stone 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".') 107b9c1b51eSKate Stone parser.add_option( 108b9c1b51eSKate Stone '-m', 109b9c1b51eSKate Stone '--module', 110b9c1b51eSKate Stone action='append', 111b9c1b51eSKate Stone type='string', 112b9c1b51eSKate Stone metavar='MODULE', 113b9c1b51eSKate Stone dest='modules', 114b9c1b51eSKate Stone help='Specify one or more modules which will be used to verify the types.', 115b9c1b51eSKate Stone default=[]) 116b9c1b51eSKate Stone parser.add_option( 117b9c1b51eSKate Stone '-d', 118b9c1b51eSKate Stone '--debug', 119b9c1b51eSKate Stone action='store_true', 120b9c1b51eSKate Stone dest='debug', 121b9c1b51eSKate Stone help='Pause 10 seconds to wait for a debugger to attach.', 122b9c1b51eSKate Stone default=False) 123b9c1b51eSKate Stone parser.add_option( 124b9c1b51eSKate Stone '-t', 125b9c1b51eSKate Stone '--type', 126b9c1b51eSKate Stone action='append', 127b9c1b51eSKate Stone type='string', 128b9c1b51eSKate Stone metavar='TYPENAME', 129b9c1b51eSKate Stone dest='typenames', 130b9c1b51eSKate Stone 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.', 131b9c1b51eSKate Stone default=[]) 132b9c1b51eSKate Stone parser.add_option( 133b9c1b51eSKate Stone '-v', 134b9c1b51eSKate Stone '--verbose', 135b9c1b51eSKate Stone action='store_true', 136b9c1b51eSKate Stone dest='verbose', 137b9c1b51eSKate Stone help='Enable verbose logging and information.', 138b9c1b51eSKate Stone default=False) 139b9c1b51eSKate Stone parser.add_option( 140b9c1b51eSKate Stone '-s', 141b9c1b51eSKate Stone '--skip-type-regex', 142b9c1b51eSKate Stone action="callback", 143b9c1b51eSKate Stone callback=regex_option_callback, 144b9c1b51eSKate Stone type='string', 145b9c1b51eSKate Stone metavar='REGEX', 146b9c1b51eSKate Stone dest='skip_type_regexes', 147b9c1b51eSKate Stone help='Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.', 148b9c1b51eSKate Stone default=[]) 149b9c1b51eSKate Stone parser.add_option( 150b9c1b51eSKate Stone '--std', 151b9c1b51eSKate Stone action="callback", 152b9c1b51eSKate Stone callback=regex_option_callback, 153b9c1b51eSKate Stone metavar='REGEX', 154b9c1b51eSKate Stone dest='skip_type_regexes', 155b9c1b51eSKate Stone help="Don't' recurse into types in the std namespace.", 156b9c1b51eSKate Stone default=[]) 157d8c3d4b1SGreg Clayton return parser 158d8c3d4b1SGreg Clayton 159b9c1b51eSKate Stone 16057d1c486SGreg Claytondef verify_type(target, options, type): 161525cd59fSSerge Guelton print(type) 162d8c3d4b1SGreg Clayton typename = type.GetName() 163d8c3d4b1SGreg Clayton # print 'type: %s' % (typename) 164b9c1b51eSKate Stone (end_offset, padding) = verify_type_recursive( 165b9c1b51eSKate Stone target, options, type, None, 0, 0, 0) 166d8c3d4b1SGreg Clayton byte_size = type.GetByteSize() 167d8c3d4b1SGreg Clayton # if end_offset < byte_size: 168d8c3d4b1SGreg Clayton # last_member_padding = byte_size - end_offset 169d8c3d4b1SGreg Clayton # print '%+4u <%u> padding' % (end_offset, last_member_padding) 170d8c3d4b1SGreg Clayton # padding += last_member_padding 171525cd59fSSerge Guelton print('Total byte size: %u' % (byte_size)) 172525cd59fSSerge Guelton print('Total pad bytes: %u' % (padding)) 173d8c3d4b1SGreg Clayton if padding > 0: 174525cd59fSSerge Guelton print('Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0)) 175525cd59fSSerge Guelton print() 176d8c3d4b1SGreg Clayton 177b9c1b51eSKate Stone 178b9c1b51eSKate Stonedef verify_type_recursive( 179b9c1b51eSKate Stone target, 180b9c1b51eSKate Stone options, 181b9c1b51eSKate Stone type, 182b9c1b51eSKate Stone member_name, 183b9c1b51eSKate Stone depth, 184b9c1b51eSKate Stone base_offset, 185b9c1b51eSKate Stone padding): 186d8c3d4b1SGreg Clayton prev_end_offset = base_offset 187d8c3d4b1SGreg Clayton typename = type.GetName() 188d8c3d4b1SGreg Clayton byte_size = type.GetByteSize() 189d8c3d4b1SGreg Clayton if member_name and member_name != typename: 190525cd59fSSerge Guelton print('%+4u <%3u> %s%s %s;' % (base_offset, byte_size, ' ' * depth, typename, member_name)) 191d8c3d4b1SGreg Clayton else: 192525cd59fSSerge Guelton print('%+4u {%3u} %s%s' % (base_offset, byte_size, ' ' * depth, typename)) 19357d1c486SGreg Clayton 19457d1c486SGreg Clayton for type_regex in options.skip_type_regexes: 19557d1c486SGreg Clayton match = type_regex.match(typename) 19657d1c486SGreg Clayton if match: 19757d1c486SGreg Clayton return (base_offset + byte_size, padding) 198d8c3d4b1SGreg Clayton 199d8c3d4b1SGreg Clayton members = type.members 200d8c3d4b1SGreg Clayton if members: 201d8c3d4b1SGreg Clayton for member_idx, member in enumerate(members): 202d8c3d4b1SGreg Clayton member_type = member.GetType() 203d8c3d4b1SGreg Clayton member_canonical_type = member_type.GetCanonicalType() 204d8c3d4b1SGreg Clayton member_type_class = member_canonical_type.GetTypeClass() 205d8c3d4b1SGreg Clayton member_name = member.GetName() 206d8c3d4b1SGreg Clayton member_offset = member.GetOffsetInBytes() 207d8c3d4b1SGreg Clayton member_total_offset = member_offset + base_offset 208d8c3d4b1SGreg Clayton member_byte_size = member_type.GetByteSize() 20957d1c486SGreg Clayton member_is_class_or_struct = False 210d8c3d4b1SGreg Clayton if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass: 21157d1c486SGreg Clayton member_is_class_or_struct = True 212b9c1b51eSKate Stone if member_idx == 0 and member_offset == target.GetAddressByteSize( 213b9c1b51eSKate Stone ) and type.IsPolymorphicClass(): 214d8c3d4b1SGreg Clayton ptr_size = target.GetAddressByteSize() 215525cd59fSSerge Guelton print('%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))) 216d8c3d4b1SGreg Clayton prev_end_offset = ptr_size 217d8c3d4b1SGreg Clayton else: 218d8c3d4b1SGreg Clayton if prev_end_offset < member_total_offset: 219d8c3d4b1SGreg Clayton member_padding = member_total_offset - prev_end_offset 220d8c3d4b1SGreg Clayton padding = padding + member_padding 221525cd59fSSerge Guelton print('%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1))) 222d8c3d4b1SGreg Clayton 22357d1c486SGreg Clayton if member_is_class_or_struct: 224b9c1b51eSKate Stone (prev_end_offset, 225b9c1b51eSKate Stone padding) = verify_type_recursive(target, 226b9c1b51eSKate Stone options, 227b9c1b51eSKate Stone member_canonical_type, 228b9c1b51eSKate Stone member_name, 229b9c1b51eSKate Stone depth + 1, 230b9c1b51eSKate Stone member_total_offset, 231b9c1b51eSKate Stone padding) 232d8c3d4b1SGreg Clayton else: 233d8c3d4b1SGreg Clayton prev_end_offset = member_total_offset + member_byte_size 23457d1c486SGreg Clayton member_typename = member_type.GetName() 235d8c3d4b1SGreg Clayton if member.IsBitfield(): 236525cd59fSSerge Guelton print('%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member.GetBitfieldSizeInBits(), member_name)) 237d8c3d4b1SGreg Clayton else: 238525cd59fSSerge Guelton print('%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member_name)) 239d8c3d4b1SGreg Clayton 240d8c3d4b1SGreg Clayton if prev_end_offset < byte_size: 241d8c3d4b1SGreg Clayton last_member_padding = byte_size - prev_end_offset 242525cd59fSSerge Guelton print('%+4u <%3u> %s<PADDING>' % (prev_end_offset, last_member_padding, ' ' * (depth + 1))) 243d8c3d4b1SGreg Clayton padding += last_member_padding 244d8c3d4b1SGreg Clayton else: 245d8c3d4b1SGreg Clayton if type.IsPolymorphicClass(): 246d8c3d4b1SGreg Clayton ptr_size = target.GetAddressByteSize() 247525cd59fSSerge Guelton print('%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))) 248d8c3d4b1SGreg Clayton prev_end_offset = ptr_size 24957d1c486SGreg Clayton prev_end_offset = base_offset + byte_size 250d8c3d4b1SGreg Clayton 251d8c3d4b1SGreg Clayton return (prev_end_offset, padding) 252d8c3d4b1SGreg Clayton 253b9c1b51eSKate Stone 25457d1c486SGreg Claytondef check_padding_command(debugger, command, result, dict): 255d8c3d4b1SGreg Clayton # Use the Shell Lexer to properly parse up command options just like a 256d8c3d4b1SGreg Clayton # shell would 257d8c3d4b1SGreg Clayton command_args = shlex.split(command) 25857d1c486SGreg Clayton parser = create_types_options(True) 259d8c3d4b1SGreg Clayton try: 260d8c3d4b1SGreg Clayton (options, args) = parser.parse_args(command_args) 261d8c3d4b1SGreg Clayton except: 262d8c3d4b1SGreg Clayton # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit 263d8c3d4b1SGreg Clayton # (courtesy of OptParse dealing with argument errors by throwing SystemExit) 264d8c3d4b1SGreg Clayton result.SetStatus(lldb.eReturnStatusFailed) 265b9c1b51eSKate Stone # returning a string is the same as returning an error whose 266b9c1b51eSKate Stone # description is the string 267b9c1b51eSKate Stone return "option parsing failed" 268811051e3SGreg Clayton verify_types(debugger.GetSelectedTarget(), options) 269d8c3d4b1SGreg Clayton 270b9c1b51eSKate Stone 27109effdacSGreg Clayton@lldb.command("parse_all_struct_class_types") 27209effdacSGreg Claytondef parse_all_struct_class_types(debugger, command, result, dict): 27309effdacSGreg Clayton command_args = shlex.split(command) 27409effdacSGreg Clayton for f in command_args: 27509effdacSGreg Clayton error = lldb.SBError() 27609effdacSGreg Clayton target = debugger.CreateTarget(f, None, None, False, error) 27709effdacSGreg Clayton module = target.GetModuleAtIndex(0) 278525cd59fSSerge Guelton print("Parsing all types in '%s'" % (module)) 27909effdacSGreg Clayton types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) 28009effdacSGreg Clayton for t in types: 281525cd59fSSerge Guelton print(t) 282525cd59fSSerge Guelton print("") 28309effdacSGreg Clayton 284d8c3d4b1SGreg Clayton 28557d1c486SGreg Claytondef verify_types(target, options): 28657d1c486SGreg Clayton 28757d1c486SGreg Clayton if not target: 288525cd59fSSerge Guelton print('error: invalid target') 28957d1c486SGreg Clayton return 290d8c3d4b1SGreg Clayton 291d8c3d4b1SGreg Clayton modules = list() 292d8c3d4b1SGreg Clayton if len(options.modules) == 0: 293d8c3d4b1SGreg Clayton # Append just the main executable if nothing was specified 294d8c3d4b1SGreg Clayton module = target.modules[0] 295d8c3d4b1SGreg Clayton if module: 296d8c3d4b1SGreg Clayton modules.append(module) 297d8c3d4b1SGreg Clayton else: 298d8c3d4b1SGreg Clayton for module_name in options.modules: 299d8c3d4b1SGreg Clayton module = lldb.target.module[module_name] 300d8c3d4b1SGreg Clayton if module: 301d8c3d4b1SGreg Clayton modules.append(module) 302d8c3d4b1SGreg Clayton 303d8c3d4b1SGreg Clayton if modules: 304d8c3d4b1SGreg Clayton for module in modules: 305525cd59fSSerge Guelton print('module: %s' % (module.file)) 306d8c3d4b1SGreg Clayton if options.typenames: 307d8c3d4b1SGreg Clayton for typename in options.typenames: 308d8c3d4b1SGreg Clayton types = module.FindTypes(typename) 309d8c3d4b1SGreg Clayton if types.GetSize(): 310525cd59fSSerge Guelton print('Found %u types matching "%s" in "%s"' % (len(types), typename, module.file)) 311d8c3d4b1SGreg Clayton for type in types: 31257d1c486SGreg Clayton verify_type(target, options, type) 313d8c3d4b1SGreg Clayton else: 314525cd59fSSerge Guelton print('error: no type matches "%s" in "%s"' % (typename, module.file)) 315d8c3d4b1SGreg Clayton else: 316b9c1b51eSKate Stone types = module.GetTypes( 317b9c1b51eSKate Stone lldb.eTypeClassClass | lldb.eTypeClassStruct) 318525cd59fSSerge Guelton print('Found %u types in "%s"' % (len(types), module.file)) 319d8c3d4b1SGreg Clayton for type in types: 32057d1c486SGreg Clayton verify_type(target, options, type) 321d8c3d4b1SGreg Clayton else: 322525cd59fSSerge Guelton print('error: no modules') 323d8c3d4b1SGreg Clayton 324d8c3d4b1SGreg Claytonif __name__ == '__main__': 325d8c3d4b1SGreg Clayton debugger = lldb.SBDebugger.Create() 32657d1c486SGreg Clayton parser = create_types_options(False) 32757d1c486SGreg Clayton 32857d1c486SGreg Clayton # try: 32957d1c486SGreg Clayton (options, args) = parser.parse_args(sys.argv[1:]) 33057d1c486SGreg Clayton # except: 33157d1c486SGreg Clayton # print "error: option parsing failed" 33257d1c486SGreg Clayton # sys.exit(1) 33357d1c486SGreg Clayton 334e110ad8fSGreg Clayton if options.debug: 335525cd59fSSerge Guelton print("Waiting for debugger to attach to process %d" % os.getpid()) 336e110ad8fSGreg Clayton os.kill(os.getpid(), signal.SIGSTOP) 337e110ad8fSGreg Clayton 33857d1c486SGreg Clayton for path in args: 33957d1c486SGreg Clayton # in a command - the lldb.* convenience variables are not to be used 34057d1c486SGreg Clayton # and their values (if any) are undefined 341b9c1b51eSKate Stone # this is the best practice to access those objects from within a 342b9c1b51eSKate Stone # command 34357d1c486SGreg Clayton error = lldb.SBError() 34457d1c486SGreg Clayton target = debugger.CreateTarget(path, 34557d1c486SGreg Clayton options.arch, 34657d1c486SGreg Clayton options.platform, 34757d1c486SGreg Clayton True, 34857d1c486SGreg Clayton error) 34957d1c486SGreg Clayton if error.Fail(): 350525cd59fSSerge Guelton print(error.GetCString()) 35157d1c486SGreg Clayton continue 35257d1c486SGreg Clayton verify_types(target, options) 35357d1c486SGreg Clayton 354*1441ffe6SDave Leedef __lldb_init_module(debugger, internal_dict): 355*1441ffe6SDave Lee debugger.HandleCommand( 356b9c1b51eSKate Stone 'command script add -f types.check_padding_command check_padding') 357525cd59fSSerge Guelton print('"check_padding" command installed, use the "--help" option for detailed help') 358