From 9e637a6a842607096cef0f9303ab9edaa2e367a2 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 21 Aug 2008 03:04:12 +0000 Subject: [PATCH] * 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 " 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 --- headers/private/kernel/heap.h | 7 ++ src/system/kernel/heap.cpp | 193 +++++++++++++++++++++------------- 2 files changed, 125 insertions(+), 75 deletions(-) diff --git a/headers/private/kernel/heap.h b/headers/private/kernel/heap.h index 1a7a098ddc..d2daa6983d 100644 --- a/headers/private/kernel/heap.h +++ b/headers/private/kernel/heap.h @@ -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(); diff --git a/src/system/kernel/heap.cpp b/src/system/kernel/heap.cpp index e812570d9d..f2f72988a6 100644 --- a/src/system/kernel/heap.cpp +++ b/src/system/kernel/heap.cpp @@ -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 ]\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 specifies the\n" + "address of the heap for which to print the allocations.\n", 0); #endif return B_OK; }