Added a new "heap.py" module that adds a new command line command that can find values on the heap and print out the dynamic type of the malloc block that contains the data. I will be modifying this a bit more to tweak the output and make the output more useful.

llvm-svn: 154504
This commit is contained in:
Greg Clayton 2012-04-11 16:27:06 +00:00
parent 6a0afeb9c8
commit 804de01c33
3 changed files with 195 additions and 41 deletions

View File

@ -2,6 +2,6 @@ LEVEL = ../../../test/make
DYLIB_NAME := heap
DYLIB_ONLY := YES
DYLIB_C_SOURCES := heap_find.cpp
DYLIB_CXX_SOURCES := heap_find.cpp
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,131 @@
#!/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 shlex
def heap_search(debugger, command, result, dict):
command_args = shlex.split(command)
usage = "usage: %prog [options] <PATH> [PATH ...]"
description='''This command lets you run the /bin/ls command from within lldb as a quick and easy example.'''
parser = optparse.OptionParser(description=description, prog='heap_search',usage=usage)
parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
parser.add_option('-t', '--type', type='string', dest='type', help='the type of data to search for (defaults to "pointer")', default='pointer')
parser.add_option('-o', '--po', action='store_true', dest='po', default='print the object descriptions for any matches')
try:
(options, args) = parser.parse_args(command_args)
except:
return
if args:
for data in args:
if options.type == 'pointer':
ptr = int(data, 0)
expr = 'find_pointer_in_heap(0x%x)' % ptr
#print 'expr: %s' % expr
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 = int(match_entry.addr)
if malloc_addr == 0:
break
malloc_size = int(match_entry.size)
offset = int(match_entry.offset)
dynamic_value = match_entry.addr.sbvalue.dynamic
# 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
if type_name == 'void *':
if malloc_size == 4096:
error = lldb.SBError()
data = bytearray(lldb.process.ReadMemory(malloc_addr, 16, error))
if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
print 'found %s 0x%x in (autorelease object pool) 0x%x, malloc_size = %u, offset = %u' % (options.type, ptr, malloc_addr, malloc_size, offset)
continue
print 'found %s 0x%x in malloc block 0x%x, malloc_size = %u, offset = %u' % (options.type, ptr, malloc_addr, malloc_size, offset)
else:
print 'found %s 0x%x in (%s) 0x%x, malloc_size = %u, offset = %u' % (options.type, ptr, type_name, malloc_addr, malloc_size, offset)
if options.po:
desc = dynamic_value.GetObjectDescription()
if desc:
print ' (%s) 0x%x %s\n' % (type_name, malloc_addr, desc)
else:
print '%s 0x%x was not found in any malloc blocks' % (options.type, ptr)
else:
print expr_sbvalue.error
elif options.type == 'cstring':
expr = 'find_cstring_in_heap("%s")' % data
#print 'expr: %s' % expr
expr_sbvalue = lldb.frame.EvaluateExpression (expr)
if expr_sbvalue.error.Success():
if expr_sbvalue.unsigned:
match_value = lldb.value(expr_sbvalue)
print match_value
i = 0
while 1:
match_entry = match_value[i]; i += 1
malloc_addr = int(match_entry.addr)
if malloc_addr == 0:
break
malloc_size = int(match_entry.size)
offset = int(match_entry.offset)
dynamic_value = match_entry.addr.sbvalue.dynamic
# 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
if type_name == 'void *':
print 'found %s "%s" in malloc block 0x%x, malloc_size = %u, offset = %u' % (options.type, data, malloc_addr, malloc_size, offset)
else:
print 'found %s "%s" in (%s) 0x%x, malloc_size = %u, offset = %u' % (options.type, data, type_name, malloc_addr, malloc_size, offset)
if options.po:
desc = dynamic_value.GetObjectDescription()
if desc:
print ' (%s) 0x%x %s\n' % (type_name, malloc_addr, desc)
else:
print '%s "%s" was not found in any malloc blocks' % (options.type, data)
else:
print expr_sbvalue.error
else:
print 'error: invalid type "%s"\nvalid values are "pointer", "cstring"' % options.type
sys.exit(1)
else:
print 'error: no arguments were given'
if __name__ == '__main__':
# This script is being run from the command line, create a debugger in case we are
# going to use any debugger functions in our function.
lldb.debugger = lldb.SBDebugger.Create()
ls (sys.argv)
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
debugger.HandleCommand('process load /Volumes/work/gclayton/Documents/src/lldb/examples/darwin/heap_find/libheap.dylib')
debugger.HandleCommand('command script add -f heap.heap_search heap_search')
print '"heap_search" command installed, type "heap_search --help" for detailed help'

View File

@ -42,7 +42,7 @@
// format. The address format shows pointers, and if those pointers point to
// objects that have symbols or know data contents, it will display information
// about the pointers:
/
//
// (lldb) memory read --format address --count 1 0x104000730
// 0x104000730: 0x0000000100002460 (void *)0x0000000100002488: MyString
//
@ -71,6 +71,7 @@
#include <stack_logging.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
struct range_callback_info_t;
@ -91,15 +92,23 @@ enum data_type_t
eDataTypeInteger
};
typedef struct range_contains_data_callback_info_tag
struct range_contains_data_callback_info_t
{
const uint8_t *data;
const size_t data_len;
const uint32_t align;
const data_type_t data_type;
uint32_t match_count;
} range_contains_data_callback_info_t;
};
struct malloc_match
{
void *addr;
intptr_t size;
intptr_t offset;
};
std::vector<malloc_match> g_matches;
static kern_return_t
task_peek (task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory)
@ -166,6 +175,7 @@ foreach_range_in_this_process (range_callback_t *callback, void *baton)
foreach_zone_in_this_process (&info);
}
static void
range_contains_ptr_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size)
{
@ -191,39 +201,41 @@ range_contains_ptr_callback (task_t task, void *baton, unsigned type, uint64_t p
if (memcmp (data_info->data, data, data_info->data_len) == 0)
{
++data_info->match_count;
printf ("0x%llx: ", addr);
uint32_t i;
switch (data_info->data_type)
{
case eDataTypeInteger:
{
// NOTE: little endian specific, but all darwin platforms are little endian now..
for (i=0; i<data_info->data_len; ++i)
printf (i ? "%2.2x" : "0x%2.2x", data[data_info->data_len - (i + 1)]);
}
break;
case eDataTypeBytes:
{
for (i=0; i<data_info->data_len; ++i)
printf (" %2.2x", data[i]);
}
break;
case eDataTypeCStr:
{
putchar ('"');
for (i=0; i<data_info->data_len; ++i)
{
if (isprint (data[i]))
putchar (data[i]);
else
printf ("\\x%2.2x", data[i]);
}
putchar ('"');
}
break;
}
printf (" found in malloc block 0x%llx + %llu (malloc_size = %llu)\n", ptr_addr, addr - ptr_addr, ptr_size);
malloc_match match = { (void *)ptr_addr, ptr_size, addr - ptr_addr };
g_matches.push_back(match);
// printf ("0x%llx: ", addr);
// uint32_t i;
// switch (data_info->data_type)
// {
// case eDataTypeInteger:
// {
// // NOTE: little endian specific, but all darwin platforms are little endian now..
// for (i=0; i<data_info->data_len; ++i)
// printf (i ? "%2.2x" : "0x%2.2x", data[data_info->data_len - (i + 1)]);
// }
// break;
// case eDataTypeBytes:
// {
// for (i=0; i<data_info->data_len; ++i)
// printf (" %2.2x", data[i]);
// }
// break;
// case eDataTypeCStr:
// {
// putchar ('"');
// for (i=0; i<data_info->data_len; ++i)
// {
// if (isprint (data[i]))
// putchar (data[i]);
// else
// printf ("\\x%2.2x", data[i]);
// }
// putchar ('"');
// }
// break;
//
// }
// printf (" found in malloc block 0x%llx + %llu (malloc_size = %llu)\n", ptr_addr, addr - ptr_addr, ptr_size);
}
}
}
@ -233,24 +245,35 @@ range_contains_ptr_callback (task_t task, void *baton, unsigned type, uint64_t p
}
}
uint32_t
malloc_match *
find_pointer_in_heap (intptr_t addr)
{
g_matches.clear();
range_contains_data_callback_info_t data_info = { (uint8_t *)&addr, sizeof(addr), sizeof(addr), eDataTypeInteger, 0};
range_callback_info_t info = { enumerate_range_in_zone, range_contains_ptr_callback, &data_info };
foreach_zone_in_this_process (&info);
return data_info.match_count;
if (g_matches.empty())
return NULL;
malloc_match match = { NULL, 0, 0 };
g_matches.push_back(match);
return g_matches.data();
}
uint32_t
malloc_match *
find_cstring_in_heap (const char *s)
{
if (s && s[0])
{
g_matches.clear();
range_contains_data_callback_info_t data_info = { (uint8_t *)s, strlen(s), 1, eDataTypeCStr, 0};
range_callback_info_t info = { enumerate_range_in_zone, range_contains_ptr_callback, &data_info };
foreach_zone_in_this_process (&info);
return data_info.match_count;
if (g_matches.empty())
return NULL;
malloc_match match = { NULL, 0, 0 };
g_matches.push_back(match);
return g_matches.data();
}
else
{