Added a new tool that can be loaded into a user space darwin application and allows you

to find data on the heap. To use this, make the project and then when stopped in your
lldb debug session:

(lldb) process load /path/to/libheap.dylib
(lldb) find_pointer_in_heap (0x112233000000)

This will grep everything in all active allocation blocks and print and malloc blocks that contain the pointer 0x112233000000.

This can also work for c strings:

(lldb) find_cstring_in_heap ("hello")

llvm-svn: 148523
This commit is contained in:
Greg Clayton 2012-01-20 01:31:24 +00:00
parent 456f01833b
commit 5d34322811
2 changed files with 200 additions and 0 deletions

View File

@ -0,0 +1,7 @@
LEVEL = ../../../test/make
DYLIB_NAME := heap
DYLIB_ONLY := YES
DYLIB_C_SOURCES := heap_find.c
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,193 @@
#include <assert.h>
#include <ctype.h>
#include <mach/mach.h>
#include <malloc/malloc.h>
#include <stdio.h>
#include <stdlib.h>
struct range_callback_info_t;
typedef void range_callback_t (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size);
typedef void zone_callback_t (void *info, const malloc_zone_t *zone);
typedef struct range_callback_info_tag
{
zone_callback_t *zone_callback;
range_callback_t *range_callback;
void *baton;
} range_callback_info_t;
typedef enum data_type
{
eDataTypeBytes,
eDataTypeCStr,
eDataTypeInteger
} data_type_t;
typedef struct range_contains_data_callback_info_tag
{
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;
static kern_return_t
task_peek (task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory)
{
*local_memory = (void*) remote_address;
return KERN_SUCCESS;
}
static const void
foreach_zone_in_this_process (range_callback_info_t *info)
{
//printf ("foreach_zone_in_this_process ( info->zone_callback = %p, info->range_callback = %p, info->baton = %p)", info->zone_callback, info->range_callback, info->baton);
if (info == NULL || info->zone_callback == NULL)
return;
vm_address_t *zones = NULL;
unsigned int num_zones = 0;
kern_return_t err = malloc_get_all_zones (0, task_peek, &zones, &num_zones);
if (KERN_SUCCESS == err)
{
for (unsigned int i=0; i<num_zones; ++i)
{
info->zone_callback (info, (const malloc_zone_t *)zones[i]);
}
}
}
static void
range_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, uint64_t ptr_size)
{
printf ("task = 0x%4.4x: baton = %p, type = %u, ptr_addr = 0x%llx + 0x%llu\n", task, baton, type, ptr_addr, ptr_size);
}
static void
ranges_callback (task_t task, void *baton, unsigned type, vm_range_t *ptrs, unsigned count)
{
range_callback_info_t *info = (range_callback_info_t *)baton;
while(count--) {
info->range_callback (task, info->baton, type, ptrs->address, ptrs->size);
ptrs++;
}
}
static void
enumerate_range_in_zone (void *baton, const malloc_zone_t *zone)
{
range_callback_info_t *info = (range_callback_info_t *)baton;
if (zone && zone->introspect)
zone->introspect->enumerator (mach_task_self(),
info,
MALLOC_PTR_IN_USE_RANGE_TYPE,
(vm_address_t)zone,
task_peek,
ranges_callback);
}
const void
foreach_range_in_this_process (range_callback_t *callback, void *baton)
{
range_callback_info_t info = { enumerate_range_in_zone, callback ? callback : range_callback, 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)
{
uint8_t *data = NULL;
range_contains_data_callback_info_t *data_info = (range_contains_data_callback_info_t *)baton;
if (data_info->data_len <= 0)
{
printf ("error: invalid data size: %zu\n", data_info->data_len);
}
else if (data_info->data_len > ptr_size)
{
// This block is too short to contain the data we are looking for...
return;
}
else if (task_peek (task, ptr_addr, ptr_size, (void **)&data) == KERN_SUCCESS)
{
assert (data);
const uint64_t end_addr = ptr_addr + ptr_size;
for (uint64_t addr = ptr_addr;
addr < end_addr && ((end_addr - addr) >= data_info->data_len);
addr += data_info->align, data += data_info->align)
{
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);
}
}
}
else
{
printf ("0x%llx: error: couldn't read %llu bytes\n", ptr_addr, ptr_size);
}
}
uint32_t
find_pointer_in_heap (intptr_t addr)
{
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;
}
uint32_t
find_cstring_in_heap (const char *s)
{
if (s && s[0])
{
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;
}
else
{
printf ("error: invalid argument (empty cstring)\n");
}
return 0;
}