* 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
+7
View File
@@ -19,6 +19,9 @@
// use areas for allocations bigger than 1MB // use areas for allocations bigger than 1MB
#define HEAP_AREA_USE_THRESHOLD 1 * 1024 * 1024 #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 { typedef struct heap_class_s {
const char *name; 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); void* heap_memalign(heap_allocator* heap, size_t alignment, size_t size);
status_t heap_free(heap_allocator* heap, void* address); 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(addr_t heapBase, size_t heapSize);
status_t heap_init_post_sem(); status_t heap_init_post_sem();
status_t heap_init_post_thread(); status_t heap_init_post_thread();
+118 -75
View File
@@ -38,8 +38,6 @@
#define PARANOID_KERNEL_FREE 1 #define PARANOID_KERNEL_FREE 1
// validate sanity of the heap after each operation (slow!) // validate sanity of the heap after each operation (slow!)
#define PARANOID_HEAP_VALIDATION 0 #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 #if KERNEL_HEAP_LEAK_CHECK
typedef struct heap_leak_check_info_s { typedef struct heap_leak_check_info_s {
@@ -111,6 +109,10 @@ struct heap_allocator_s {
uint32 total_free_pages; uint32 total_free_pages;
uint32 empty_areas; uint32 empty_areas;
#if KERNEL_HEAP_LEAK_CHECK
addr_t (*get_caller)();
#endif
heap_bin * bins; heap_bin * bins;
heap_area * areas; // sorted so that the desired area is always first heap_area * areas; // sorted so that the desired area is always first
heap_area * all_areas; // all areas including full ones 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 static bool
dump_allocations_per_caller(int argc, char **argv) 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++) { addr_t base = area->base + i * heap->page_size;
if (strcmp(argv[i], "-c") == 0) { if (page->bin_index < heap->bin_count) {
sortBySize = false; // page is used by a small allocation bin
} else { uint32 elementCount = page->empty_index;
print_debugger_command_usage(argv[0]); size_t elementSize = heap->bins[page->bin_index].element_size;
return 0; 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;
sCallerInfoCount = 0; temp = (addr_t *)*temp) {
if ((addr_t)temp == base) {
for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT; classIndex++) { elementInUse = false;
heap_allocator *heap = sHeaps[classIndex]; break;
// 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;
}
} }
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 if (!elementInUse)
* heap->page_size - sizeof(heap_leak_check_info)); continue;
info = (heap_leak_check_info *)(base + elementSize
- sizeof(heap_leak_check_info));
caller_info* callerInfo = get_caller_info(info->caller); caller_info* callerInfo = get_caller_info(info->caller);
if (callerInfo == NULL) { if (callerInfo == NULL) {
@@ -603,13 +568,77 @@ dump_allocations_per_caller(int argc, char **argv)
callerInfo->count++; callerInfo->count++;
callerInfo->size += info->size; 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->areas = heap->all_areas = NULL;
heap->bins = (heap_bin *)base; heap->bins = (heap_bin *)base;
#if KERNEL_HEAP_LEAK_CHECK
heap->get_caller = &get_caller;
#endif
heap->bin_count = 0; heap->bin_count = 0;
size_t binSize = 0, lastSize = 0; size_t binSize = 0, lastSize = 0;
uint32 count = heap->page_size / heapClass->min_bin_size; 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->size = size - sizeof(heap_leak_check_info);
info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id()); info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id());
info->team = (gKernelStartup ? 0 : team_get_current_team_id()); info->team = (gKernelStartup ? 0 : team_get_current_team_id());
info->caller = get_caller(); info->caller = heap->get_caller();
#endif #endif
return address; 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->size = size - sizeof(heap_leak_check_info);
info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id()); info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id());
info->team = (gKernelStartup ? 0 : team_get_current_team_id()); info->team = (gKernelStartup ? 0 : team_get_current_team_id());
info->caller = get_caller(); info->caller = heap->get_caller();
#endif #endif
return (void *)(firstPage->area->base + firstPage->index * heap->page_size); 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 static status_t
heap_realloc(heap_allocator *heap, void *address, void **newAddress, heap_realloc(heap_allocator *heap, void *address, void **newAddress,
size_t newSize) size_t newSize)
@@ -1685,10 +1727,11 @@ heap_init(addr_t base, size_t size)
add_debugger_command_etc("allocations_per_caller", add_debugger_command_etc("allocations_per_caller",
&dump_allocations_per_caller, &dump_allocations_per_caller,
"Dump current allocations summed up 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" "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" "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 #endif
return B_OK; return B_OK;
} }