* Added heap_set_get_caller() which can be used when heap leak checking

is enabled to set a per-heap get_caller() function.
* Added "-h <heap>" option to the "allocations_per_caller" command. If
  given only the allocation for the specified heap are considered.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27097 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2008-08-21 03:04:12 +00:00
parent 82427071c1
commit 9e637a6a84
2 changed files with 125 additions and 75 deletions

View File

@ -19,6 +19,9 @@
// use areas for allocations bigger than 1MB
#define HEAP_AREA_USE_THRESHOLD 1 * 1024 * 1024
// store size, thread and team info at the end of each allocation block
#define KERNEL_HEAP_LEAK_CHECK 0
typedef struct heap_class_s {
const char *name;
@ -55,6 +58,10 @@ heap_allocator* heap_create_allocator(const char* name, addr_t base,
void* heap_memalign(heap_allocator* heap, size_t alignment, size_t size);
status_t heap_free(heap_allocator* heap, void* address);
#if KERNEL_HEAP_LEAK_CHECK
void heap_set_get_caller(heap_allocator* heap, addr_t (*getCaller)());
#endif
status_t heap_init(addr_t heapBase, size_t heapSize);
status_t heap_init_post_sem();
status_t heap_init_post_thread();

View File

@ -38,8 +38,6 @@
#define PARANOID_KERNEL_FREE 1
// validate sanity of the heap after each operation (slow!)
#define PARANOID_HEAP_VALIDATION 0
// store size, thread and team info at the end of each allocation block
#define KERNEL_HEAP_LEAK_CHECK 0
#if KERNEL_HEAP_LEAK_CHECK
typedef struct heap_leak_check_info_s {
@ -111,6 +109,10 @@ struct heap_allocator_s {
uint32 total_free_pages;
uint32 empty_areas;
#if KERNEL_HEAP_LEAK_CHECK
addr_t (*get_caller)();
#endif
heap_bin * bins;
heap_area * areas; // sorted so that the desired area is always first
heap_area * all_areas; // all areas including full ones
@ -524,76 +526,39 @@ caller_info_compare_count(const void* _a, const void* _b)
}
static int
dump_allocations_per_caller(int argc, char **argv)
static bool
analyze_allocation_callers(heap_allocator* heap)
{
bool sortBySize = true;
// go through all the pages in all the areas
heap_area *area = heap->all_areas;
while (area) {
heap_leak_check_info *info = NULL;
for (uint32 i = 0; i < area->page_count; i++) {
heap_page *page = &area->page_table[i];
if (!page->in_use)
continue;
for (int32 i = 1; i < argc; i++) {
if (strcmp(argv[i], "-c") == 0) {
sortBySize = false;
} else {
print_debugger_command_usage(argv[0]);
return 0;
}
}
sCallerInfoCount = 0;
for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT; classIndex++) {
heap_allocator *heap = sHeaps[classIndex];
// go through all the pages in all the areas
heap_area *area = heap->all_areas;
while (area) {
heap_leak_check_info *info = NULL;
for (uint32 i = 0; i < area->page_count; i++) {
heap_page *page = &area->page_table[i];
if (!page->in_use)
continue;
addr_t base = area->base + i * heap->page_size;
if (page->bin_index < heap->bin_count) {
// page is used by a small allocation bin
uint32 elementCount = page->empty_index;
size_t elementSize = heap->bins[page->bin_index].element_size;
for (uint32 j = 0; j < elementCount; j++, base += elementSize) {
// walk the free list to see if this element is in use
bool elementInUse = true;
for (addr_t *temp = page->free_list; temp != NULL;
temp = (addr_t *)*temp) {
if ((addr_t)temp == base) {
elementInUse = false;
break;
}
addr_t base = area->base + i * heap->page_size;
if (page->bin_index < heap->bin_count) {
// page is used by a small allocation bin
uint32 elementCount = page->empty_index;
size_t elementSize = heap->bins[page->bin_index].element_size;
for (uint32 j = 0; j < elementCount; j++, base += elementSize) {
// walk the free list to see if this element is in use
bool elementInUse = true;
for (addr_t *temp = page->free_list; temp != NULL;
temp = (addr_t *)*temp) {
if ((addr_t)temp == base) {
elementInUse = false;
break;
}
if (!elementInUse)
continue;
info = (heap_leak_check_info *)(base + elementSize
- sizeof(heap_leak_check_info));
caller_info* callerInfo = get_caller_info(info->caller);
if (callerInfo == NULL) {
kprintf("out of space for caller infos\n");
return 0;
}
callerInfo->count++;
callerInfo->size += info->size;
}
} else {
// page is used by a big allocation, find the page count
uint32 pageCount = 1;
while (i + pageCount < area->page_count
&& area->page_table[i + pageCount].in_use
&& area->page_table[i + pageCount].bin_index == heap->bin_count
&& area->page_table[i + pageCount].allocation_id == page->allocation_id)
pageCount++;
info = (heap_leak_check_info *)(base + pageCount
* heap->page_size - sizeof(heap_leak_check_info));
if (!elementInUse)
continue;
info = (heap_leak_check_info *)(base + elementSize
- sizeof(heap_leak_check_info));
caller_info* callerInfo = get_caller_info(info->caller);
if (callerInfo == NULL) {
@ -603,13 +568,77 @@ dump_allocations_per_caller(int argc, char **argv)
callerInfo->count++;
callerInfo->size += info->size;
// skip the allocated pages
i += pageCount - 1;
}
} else {
// page is used by a big allocation, find the page count
uint32 pageCount = 1;
while (i + pageCount < area->page_count
&& area->page_table[i + pageCount].in_use
&& area->page_table[i + pageCount].bin_index
== heap->bin_count
&& area->page_table[i + pageCount].allocation_id
== page->allocation_id) {
pageCount++;
}
info = (heap_leak_check_info *)(base + pageCount
* heap->page_size - sizeof(heap_leak_check_info));
caller_info* callerInfo = get_caller_info(info->caller);
if (callerInfo == NULL) {
kprintf("out of space for caller infos\n");
return false;
}
callerInfo->count++;
callerInfo->size += info->size;
// skip the allocated pages
i += pageCount - 1;
}
}
area = area->all_next;
}
return true;
}
static int
dump_allocations_per_caller(int argc, char **argv)
{
bool sortBySize = true;
heap_allocator* heap = NULL;
for (int32 i = 1; i < argc; i++) {
if (strcmp(argv[i], "-c") == 0) {
sortBySize = false;
} else if (strcmp(argv[i], "-h") == 0) {
uint64 heapAddress;
if (++i >= argc
|| !evaluate_debug_expression(argv[i], &heapAddress, true)) {
print_debugger_command_usage(argv[0]);
return 0;
}
area = area->all_next;
heap = (heap_allocator*)(addr_t)heapAddress;
} else {
print_debugger_command_usage(argv[0]);
return 0;
}
}
sCallerInfoCount = 0;
if (heap != NULL) {
if (!analyze_allocation_callers(heap))
return 0;
} else {
for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT;
classIndex++) {
if (!analyze_allocation_callers(sHeaps[classIndex]))
return 0;
}
}
@ -964,6 +993,10 @@ heap_create_allocator(const char *name, addr_t base, size_t size,
heap->areas = heap->all_areas = NULL;
heap->bins = (heap_bin *)base;
#if KERNEL_HEAP_LEAK_CHECK
heap->get_caller = &get_caller;
#endif
heap->bin_count = 0;
size_t binSize = 0, lastSize = 0;
uint32 count = heap->page_size / heapClass->min_bin_size;
@ -1203,7 +1236,7 @@ heap_raw_alloc(heap_allocator *heap, size_t size, uint32 binIndex)
info->size = size - sizeof(heap_leak_check_info);
info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id());
info->team = (gKernelStartup ? 0 : team_get_current_team_id());
info->caller = get_caller();
info->caller = heap->get_caller();
#endif
return address;
}
@ -1223,7 +1256,7 @@ heap_raw_alloc(heap_allocator *heap, size_t size, uint32 binIndex)
info->size = size - sizeof(heap_leak_check_info);
info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id());
info->team = (gKernelStartup ? 0 : team_get_current_team_id());
info->caller = get_caller();
info->caller = heap->get_caller();
#endif
return (void *)(firstPage->area->base + firstPage->index * heap->page_size);
}
@ -1457,6 +1490,15 @@ heap_free(heap_allocator *heap, void *address)
}
#if KERNEL_HEAP_LEAK_CHECK
extern "C" void
heap_set_get_caller(heap_allocator* heap, addr_t (*getCaller)())
{
heap->get_caller = getCaller;
}
#endif
static status_t
heap_realloc(heap_allocator *heap, void *address, void **newAddress,
size_t newSize)
@ -1685,10 +1727,11 @@ heap_init(addr_t base, size_t size)
add_debugger_command_etc("allocations_per_caller",
&dump_allocations_per_caller,
"Dump current allocations summed up per caller",
"[ \"-c\" ]\n"
"[ \"-c\" ] [ -h <heap> ]\n"
"The current allocations will by summed up by caller (their count and\n"
"size) printed in decreasing order by size or, if \"-c\" is\n"
"specified, by allocation count.\n", 0);
"specified, by allocation count. If given <heap> specifies the\n"
"address of the heap for which to print the allocations.\n", 0);
#endif
return B_OK;
}