forked from OSchip/llvm-project
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
This commit is contained in:
parent
8217757379
commit
57d1c4865e
lldb/examples/python
|
@ -12,6 +12,7 @@
|
||||||
import commands
|
import commands
|
||||||
import platform
|
import platform
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -50,26 +51,48 @@ import optparse
|
||||||
import shlex
|
import shlex
|
||||||
import time
|
import time
|
||||||
|
|
||||||
def create_types_options():
|
def regex_option_callback(option, opt_str, value, parser):
|
||||||
usage = "usage: %prog [options]"
|
if opt_str == "--std":
|
||||||
description='''This command will help you verify that types in your program
|
value = '^std::'
|
||||||
are packed efficiently by showing all types and their sizes and showing the
|
regex = re.compile(value)
|
||||||
padding bytes that waste space.
|
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 = 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)
|
if not for_lldb_command:
|
||||||
parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='specify one platform by name')
|
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('-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('-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
|
return parser
|
||||||
|
|
||||||
def verify_type (target, type):
|
def verify_type (target, options, type):
|
||||||
print type
|
print type
|
||||||
typename = type.GetName()
|
typename = type.GetName()
|
||||||
# print 'type: %s' % (typename)
|
# 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()
|
byte_size = type.GetByteSize()
|
||||||
# if end_offset < byte_size:
|
# if end_offset < byte_size:
|
||||||
# last_member_padding = byte_size - end_offset
|
# 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 'Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0)
|
||||||
print
|
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
|
prev_end_offset = base_offset
|
||||||
typename = type.GetName()
|
typename = type.GetName()
|
||||||
byte_size = type.GetByteSize()
|
byte_size = type.GetByteSize()
|
||||||
if member_name and member_name != typename:
|
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:
|
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
|
members = type.members
|
||||||
if members:
|
if members:
|
||||||
for member_idx, member in enumerate(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_offset = member.GetOffsetInBytes()
|
||||||
member_total_offset = member_offset + base_offset
|
member_total_offset = member_offset + base_offset
|
||||||
member_byte_size = member_type.GetByteSize()
|
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:
|
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():
|
if member_idx == 0 and member_offset == target.GetAddressByteSize() and type.IsPolymorphicClass():
|
||||||
ptr_size = target.GetAddressByteSize()
|
ptr_size = target.GetAddressByteSize()
|
||||||
print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))
|
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
|
padding = padding + member_padding
|
||||||
print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1))
|
print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1))
|
||||||
|
|
||||||
if is_class_or_struct:
|
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)
|
(prev_end_offset, padding) = verify_type_recursive (target, options, member_canonical_type, member_name, depth + 1, member_total_offset, padding)
|
||||||
else:
|
else:
|
||||||
prev_end_offset = member_total_offset + member_byte_size
|
prev_end_offset = member_total_offset + member_byte_size
|
||||||
|
member_typename = member_type.GetName()
|
||||||
if member.IsBitfield():
|
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:
|
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:
|
if prev_end_offset < byte_size:
|
||||||
last_member_padding = byte_size - prev_end_offset
|
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()
|
ptr_size = target.GetAddressByteSize()
|
||||||
print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))
|
print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1))
|
||||||
prev_end_offset = ptr_size
|
prev_end_offset = ptr_size
|
||||||
prev_end_offset = base_offset + type.GetByteSize()
|
prev_end_offset = base_offset + byte_size
|
||||||
|
|
||||||
return (prev_end_offset, padding)
|
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
|
# Use the Shell Lexer to properly parse up command options just like a
|
||||||
# shell would
|
# shell would
|
||||||
command_args = shlex.split(command)
|
command_args = shlex.split(command)
|
||||||
verify_types(debugger, command_args)
|
parser = create_types_options(True)
|
||||||
|
|
||||||
def verify_types (debugger, command_args):
|
|
||||||
|
|
||||||
parser = create_types_options()
|
|
||||||
try:
|
try:
|
||||||
(options, args) = parser.parse_args(command_args)
|
(options, args) = parser.parse_args(command_args)
|
||||||
except:
|
except:
|
||||||
|
@ -151,13 +176,57 @@ def verify_types (debugger, command_args):
|
||||||
# (courtesy of OptParse dealing with argument errors by throwing SystemExit)
|
# (courtesy of OptParse dealing with argument errors by throwing SystemExit)
|
||||||
result.SetStatus (lldb.eReturnStatusFailed)
|
result.SetStatus (lldb.eReturnStatusFailed)
|
||||||
return "option parsing failed" # returning a string is the same as returning an error whose description is the string
|
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:
|
for path in args:
|
||||||
# in a command - the lldb.* convenience variables are not to be used
|
# in a command - the lldb.* convenience variables are not to be used
|
||||||
# and their values (if any) are undefined
|
# and their values (if any) are undefined
|
||||||
|
@ -171,42 +240,8 @@ def verify_types (debugger, command_args):
|
||||||
if error.Fail():
|
if error.Fail():
|
||||||
print error.GetCString()
|
print error.GetCString()
|
||||||
continue
|
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):
|
elif getattr(lldb, 'debugger', None):
|
||||||
lldb.debugger.HandleCommand('command script add -f types.types_command types')
|
lldb.debugger.HandleCommand('command script add -f types.check_padding_command check_padding')
|
||||||
print '"types" command installed, use the "--help" option for detailed help'
|
print '"check_padding" command installed, use the "--help" option for detailed help'
|
Loading…
Reference in New Issue