blob: f4020edc049d967bbf0ce7ca43e327986a18f1c3 [file] [log] [blame]
#!/usr/bin/python
#----------------------------------------------------------------------
# Be sure to add the python path that points to the LLDB shared library.
#
# # To use this in the embedded python interpreter using "lldb" just
# import it with the full path using the "command script import"
# command
# (lldb) command script import /path/to/heap.py
#
# For the shells csh, tcsh:
# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./heap.py )
#
# For the shells sh, bash:
# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./heap.py
#----------------------------------------------------------------------
import lldb
import commands
import optparse
import os
import shlex
def add_common_options(parser):
parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False)
parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False)
parser.add_option('-f', '--format', type='string', dest='format', help='the format to use when dumping memory if --memory is specified', default=None)
def heap_search(options, arg_str):
expr = None
arg_str_description = arg_str
default_memory_format = "Y" # 'Y' is "bytes with ASCII" format
#memory_chunk_size = 1
if options.type == 'pointer':
expr = 'find_pointer_in_heap((void *)%s)' % arg_str
arg_str_description = 'malloc block containing pointer %s' % arg_str
default_memory_format = "A" # 'A' is "address" format
#memory_chunk_size = lldb.process.GetAddressByteSize()
elif options.type == 'cstr':
expr = 'find_cstring_in_heap("%s")' % arg_str
arg_str_description = 'malloc block containing "%s"' % arg_str
elif options.type == 'addr':
expr = 'find_block_for_address((void *)%s)' % arg_str
arg_str_description = 'malloc block for %s' % arg_str
else:
print 'error: invalid type "%s"\nvalid values are "pointer", "cstr"' % options.type
return
expr_sbvalue = lldb.frame.EvaluateExpression (expr)
if expr_sbvalue.error.Success():
if expr_sbvalue.unsigned:
match_value = lldb.value(expr_sbvalue)
i = 0
while 1:
match_entry = match_value[i]; i += 1
malloc_addr = match_entry.addr.sbvalue.unsigned
if malloc_addr == 0:
break
malloc_size = int(match_entry.size)
offset = int(match_entry.offset)
dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget)
# If the type is still 'void *' then we weren't able to figure
# out a dynamic type for the malloc_addr
type_name = dynamic_value.type.name
description = '[%u] %s: addr = 0x%x' % (i, arg_str_description, malloc_addr)
if offset != 0:
description += ' + %u' % (offset)
description += ', size = %u' % (malloc_size)
if type_name == 'void *':
if options.type == 'pointer' and malloc_size == 4096:
error = lldb.SBError()
data = bytearray(lldb.process.ReadMemory(malloc_addr, 16, error))
if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
description += ', type = (AUTORELEASE!)'
print description
else:
description += ', type = %s' % (type_name)
derefed_dynamic_value = dynamic_value.deref
ivar_member = None
if derefed_dynamic_value:
derefed_dynamic_type = derefed_dynamic_value.type
member = derefed_dynamic_type.GetFieldAtIndex(0)
search_bases = False
if member:
if member.GetOffsetInBytes() <= offset:
for field_idx in range (derefed_dynamic_type.GetNumberOfFields()):
member = derefed_dynamic_type.GetFieldAtIndex(field_idx)
member_byte_offset = member.GetOffsetInBytes()
if member_byte_offset == offset:
ivar_member = member
break
else:
search_bases = True
else:
search_bases = True
if not ivar_member and search_bases:
for field_idx in range (derefed_dynamic_type.GetNumberOfDirectBaseClasses()):
member = derefed_dynamic_type.GetDirectBaseClassAtIndex(field_idx)
member_byte_offset = member.GetOffsetInBytes()
if member_byte_offset == offset:
ivar_member = member
break
if not ivar_member:
for field_idx in range (derefed_dynamic_type.GetNumberOfVirtualBaseClasses()):
member = derefed_dynamic_type.GetVirtualBaseClassAtIndex(field_idx)
member_byte_offset = member.GetOffsetInBytes()
if member_byte_offset == offset:
ivar_member = member
break
if ivar_member:
description +=', ivar = %s' % (ivar_member.name)
print description
if derefed_dynamic_value:
print derefed_dynamic_value
if options.print_object_description:
desc = dynamic_value.GetObjectDescription()
if desc:
print ' (%s) 0x%x %s\n' % (type_name, malloc_addr, desc)
if options.memory:
memory_format = options.format
if not memory_format:
memory_format = default_memory_format
cmd_result = lldb.SBCommandReturnObject()
#count = malloc_size / memory_chunk_size
memory_command = "memory read -f %s 0x%x 0x%x" % (memory_format, malloc_addr, malloc_addr + malloc_size)
lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
print cmd_result.GetOutput()
else:
print '%s %s was not found in any malloc blocks' % (options.type, arg_str)
else:
print expr_sbvalue.error
print
def ptr_refs(debugger, command, result, dict):
command_args = shlex.split(command)
usage = "usage: %prog [options] <PTR> [PTR ...]"
description='''Searches the heap for pointer references on darwin user space programs.
Any matches that were found will dump the malloc blocks that contain the pointers
and might be able to print what kind of objects the pointers are contained in using
dynamic type information in the program.'''
parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage)
add_common_options(parser)
try:
(options, args) = parser.parse_args(command_args)
except:
return
options.type = 'pointer'
if args:
for data in args:
heap_search (options, data)
else:
print 'error: no pointer arguments were given'
def cstr_refs(debugger, command, result, dict):
command_args = shlex.split(command)
usage = "usage: %prog [options] <CSTR> [CSTR ...]"
description='''Searches the heap for C string references on darwin user space programs.
Any matches that were found will dump the malloc blocks that contain the C strings
and might be able to print what kind of objects the pointers are contained in using
dynamic type information in the program.'''
parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
add_common_options(parser)
try:
(options, args) = parser.parse_args(command_args)
except:
return
options.type = 'cstr'
if args:
for data in args:
heap_search (options, data)
else:
print 'error: no c string arguments were given to search for'
def malloc_info(debugger, command, result, dict):
command_args = shlex.split(command)
usage = "usage: %prog [options] <ADDR> [ADDR ...]"
description='''Searches the heap a malloc block that contains the addresses specified as arguments.
Any matches that were found will dump the malloc blocks that match or contain
the specified address. The matching blocks might be able to show what kind
of objects they are using dynamic type information in the program.'''
parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage)
add_common_options(parser)
try:
(options, args) = parser.parse_args(command_args)
except:
return
options.type = 'addr'
if args:
for data in args:
heap_search (options, data)
else:
print 'error: no c string arguments were given to search for'
def __lldb_init_module (debugger, dict):
# This initializer is being run from LLDB in the embedded command interpreter
# Add any commands contained in this module to LLDB
libheap_dylib_path = os.path.dirname(__file__) + '/libheap.dylib'
debugger.HandleCommand('process load "%s"' % libheap_dylib_path)
debugger.HandleCommand('command script add -f heap.ptr_refs ptr_refs')
debugger.HandleCommand('command script add -f heap.cstr_refs cstr_refs')
debugger.HandleCommand('command script add -f heap.malloc_info malloc_info')
print '"ptr_refs", "cstr_refs", and "malloc_info" commands have been installed, use the "--help" options on these commands for detailed help.'