From 57d1c4865ef17da367f35eee467e78d15cd0e800 Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Sat, 22 Jun 2013 01:42:49 +0000 Subject: [PATCH] Added a regex that can be specified to avoid showing contents on types that match. Also split things up a bit so this can be run as a stand alone script or in lldb. llvm-svn: 184628 --- lldb/examples/python/types.py | 175 ++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 70 deletions(-) diff --git a/lldb/examples/python/types.py b/lldb/examples/python/types.py index db1e8cbef12e..d2451e0e0262 100755 --- a/lldb/examples/python/types.py +++ b/lldb/examples/python/types.py @@ -12,6 +12,7 @@ import commands import platform import os +import re import sys try: @@ -50,26 +51,48 @@ import optparse import shlex import time -def create_types_options(): - usage = "usage: %prog [options]" - description='''This command will help you verify that types in your program -are packed efficiently by showing all types and their sizes and showing the -padding bytes that waste space. +def regex_option_callback(option, opt_str, value, parser): + if opt_str == "--std": + value = '^std::' + regex = re.compile(value) + parser.values.skip_type_regexes.append (regex) + +def create_types_options(for_lldb_command): + if for_lldb_command: + usage = "usage: %prog [options]" + description='''This command will help check for padding in between +base classes and members in structs and classes. It will summarize the types +and how much padding was found. If no types are specified with the --types TYPENAME +option, all structure and class types will be verified. If no modules are +specified with the --module option, only the target's main executable will be +searched. +''' + else: + usage = "usage: %prog [options] EXEPATH [EXEPATH ...]" + description='''This command will help check for padding in between +base classes and members in structures and classes. It will summarize the types +and how much padding was found. One or more paths to executable files must be +specified and targets will be created with these modules. If no types are +specified with the --types TYPENAME option, all structure and class types will +be verified in all specified modules. ''' parser = optparse.OptionParser(description=description, prog='framestats',usage=usage) - parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) - parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name') + if not for_lldb_command: + parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) + parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name') 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=[]) - 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=[]) parser.add_option('-d', '--debug', action='store_true', dest='debug', help='Pause 10 seconds to wait for a debugger to attach.', default=False) - parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False) + 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=[]) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='Enable verbose logging and information.', default=False) + 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=[]) + 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=[]) return parser -def verify_type (target, type): +def verify_type (target, options, type): print type typename = type.GetName() # print 'type: %s' % (typename) - (end_offset, padding) = verify_type_recursive (target, type, None, 0, 0, 0) + (end_offset, padding) = verify_type_recursive (target, options, type, None, 0, 0, 0) byte_size = type.GetByteSize() # if end_offset < byte_size: # last_member_padding = byte_size - end_offset @@ -81,15 +104,20 @@ def verify_type (target, type): 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): +def verify_type_recursive (target, options, type, member_name, depth, base_offset, padding): 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, type.GetByteSize(), ' ' * depth, typename, member_name) + print '%+4u <%3u> %s%s %s;' % (base_offset, byte_size, ' ' * depth, typename, member_name) else: - print '%+4u <%3u> %s%s' % (base_offset, type.GetByteSize(), ' ' * depth, typename) + print '%+4u {%3u} %s%s' % (base_offset, byte_size, ' ' * depth, typename) + for type_regex in options.skip_type_regexes: + match = type_regex.match (typename) + if match: + return (base_offset + byte_size, padding) + members = type.members if members: for member_idx, member in enumerate(members): @@ -100,9 +128,9 @@ def verify_type_recursive (target, type, member_name, depth, base_offset, paddin member_offset = member.GetOffsetInBytes() member_total_offset = member_offset + base_offset member_byte_size = member_type.GetByteSize() - is_class_or_struct = False + member_is_class_or_struct = False if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass: - is_class_or_struct = True + 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)) @@ -113,14 +141,15 @@ def verify_type_recursive (target, type, member_name, depth, base_offset, paddin padding = padding + member_padding print '%+4u <%3u> %s' % (prev_end_offset, member_padding, ' ' * (depth + 1)) - if is_class_or_struct: - (prev_end_offset, padding) = verify_type_recursive (target, member_canonical_type, member_name, depth + 1, member_total_offset, padding) + if member_is_class_or_struct: + (prev_end_offset, padding) = verify_type_recursive (target, options, member_canonical_type, member_name, depth + 1, member_total_offset, padding) 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_type.GetName(), member.GetBitfieldSizeInBits(), member_name) + 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_type.GetName(), member_name) + 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 @@ -131,19 +160,15 @@ def verify_type_recursive (target, type, member_name, depth, base_offset, paddin 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 + type.GetByteSize() + prev_end_offset = base_offset + byte_size return (prev_end_offset, padding) -def types_command (debugger, command, result, dict): +def check_padding_command (debugger, command, result, dict): # Use the Shell Lexer to properly parse up command options just like a # shell would command_args = shlex.split(command) - verify_types(debugger, command_args) - -def verify_types (debugger, command_args): - - parser = create_types_options() + parser = create_types_options(True) try: (options, args) = parser.parse_args(command_args) except: @@ -151,13 +176,57 @@ def verify_types (debugger, command_args): # (courtesy of OptParse dealing with argument errors by throwing SystemExit) result.SetStatus (lldb.eReturnStatusFailed) return "option parsing failed" # returning a string is the same as returning an error whose description is the string + verify_types(options, debugger.GetSelectedTarget(), command_args) + + +def verify_types (target, options): + + if not target: + print 'error: invalid target' + return + + modules = list() + if len(options.modules) == 0: + # Append just the main executable if nothing was specified + module = target.modules[0] + if module: + modules.append(module) + else: + for module_name in options.modules: + module = lldb.target.module[module_name] + if module: + modules.append(module) + + if modules: + for module in modules: + print 'module: %s' % (module.file) + if options.typenames: + for typename in options.typenames: + types = module.FindTypes(typename) + if types.GetSize(): + print 'Found %u types matching "%s" in "%s"' % (len(types), typename, module.file) + for type in types: + verify_type (target, options, type) + else: + print 'error: no type matches "%s" in "%s"' % (typename, module.file) + else: + types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) + print 'Found %u types in "%s"' % (len(types), module.file) + for type in types: + verify_type (target, options, type) + else: + print 'error: no modules' + +if __name__ == '__main__': + debugger = lldb.SBDebugger.Create() + parser = create_types_options(False) + + # try: + (options, args) = parser.parse_args(sys.argv[1:]) + # except: + # print "error: option parsing failed" + # sys.exit(1) - if options.debug: - print 'Waiting for debugger to attach...' - for i in range(10): - time.sleep(1) - print '.' - for path in args: # in a command - the lldb.* convenience variables are not to be used # and their values (if any) are undefined @@ -171,42 +240,8 @@ def verify_types (debugger, command_args): if error.Fail(): print error.GetCString() continue + verify_types (target, options) - modules = list() - if len(options.modules) == 0: - # Append just the main executable if nothing was specified - module = target.modules[0] - if module: - modules.append(module) - else: - for module_name in options.modules: - module = lldb.target.module[module_name] - if module: - modules.append(module) - - if modules: - for module in modules: - print 'module: %s' % (module.file) - if options.typenames: - for typename in options.typenames: - types = module.FindTypes(typename) - if types.GetSize(): - print 'Found %u types matching "%s" in "%s"' % (len(types), typename, module.file) - for type in types: - verify_type (target, type) - else: - print 'error: no type matches "%s" in "%s"' % (typename, module.file) - else: - types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) - print 'Found %u types in "%s"' % (len(types), module.file) - for type in types: - verify_type (target, type) - else: - print 'error: no modules' - -if __name__ == '__main__': - debugger = lldb.SBDebugger.Create() - verify_types (debugger, sys.argv[1:]) elif getattr(lldb, 'debugger', None): - lldb.debugger.HandleCommand('command script add -f types.types_command types') - print '"types" command installed, use the "--help" option for detailed help' \ No newline at end of file + lldb.debugger.HandleCommand('command script add -f types.check_padding_command check_padding') + print '"check_padding" command installed, use the "--help" option for detailed help' \ No newline at end of file