From ec0190adb0a9abe5a796d31468f421896eb47124 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Fri, 10 Apr 2015 17:04:28 +0200 Subject: [PATCH] malloc_debug: Implement allocation dump on exit in guarded heap. When enabled (using heap_debug_dump_allocations_on_exit(true) or MALLOC_DEBUG=e) this causes a dump of all remaining allocations when libroot_debug is unloaded. It uses terminate_after to be called as late as possible. When combined with alloc stack traces this makes for a nice if a bit crude leak checker. Note that a lot of allocations usually remain even at that stage due to statically, lazyly and globally allocated stuff from the various system libraries where it isn't necessarily worth the overhead to free them when the program terminates anyway. --- headers/posix/malloc_debug.h | 1 + headers/private/libroot/libroot_private.h | 1 + src/system/libroot/libroot_init.c | 7 +++ .../libroot/posix/malloc/arch-specific.cpp | 7 +++ .../posix/malloc_debug/guarded_heap.cpp | 50 +++++++++++++++++-- .../libroot/posix/malloc_debug/heap.cpp | 14 ++++++ 6 files changed, 76 insertions(+), 4 deletions(-) diff --git a/headers/posix/malloc_debug.h b/headers/posix/malloc_debug.h index 4c07f5af8e..5e0de0016a 100644 --- a/headers/posix/malloc_debug.h +++ b/headers/posix/malloc_debug.h @@ -30,6 +30,7 @@ void *heap_debug_malloc_with_guard_page(size_t size); status_t heap_debug_get_allocation_info(void *address, size_t *size, thread_id *thread); +status_t heap_debug_dump_allocations_on_exit(bool enabled); status_t heap_debug_set_stack_trace_depth(size_t stackTraceDepth); #ifdef __cplusplus diff --git a/headers/private/libroot/libroot_private.h b/headers/private/libroot/libroot_private.h index 7f6dcd5f3a..85036df0bf 100644 --- a/headers/private/libroot/libroot_private.h +++ b/headers/private/libroot/libroot_private.h @@ -35,6 +35,7 @@ void _call_atexit_hooks_for_range(addr_t start, addr_t size); void __init_env(const struct user_space_program_args *args); status_t __init_heap(void); void __init_heap_post_env(void); +void __heap_terminate_after(void); void __init_time(addr_t commPageTable); void __arch_init_time(struct real_time_data *data, bool setDefaults); diff --git a/src/system/libroot/libroot_init.c b/src/system/libroot/libroot_init.c index caef95b2fc..03cb17f70d 100644 --- a/src/system/libroot/libroot_init.c +++ b/src/system/libroot/libroot_init.c @@ -20,6 +20,7 @@ void initialize_before(image_id imageID); +void terminate_after(image_id imageID); struct rld_export *__gRuntimeLoader = NULL; // This little bugger is set to something meaningful by the runtime loader @@ -94,3 +95,9 @@ _init_c_library_(void) // do anything here. } + +void +terminate_after(image_id id) +{ + __heap_terminate_after(); +} diff --git a/src/system/libroot/posix/malloc/arch-specific.cpp b/src/system/libroot/posix/malloc/arch-specific.cpp index 4b9572e868..d2c25b8f5e 100644 --- a/src/system/libroot/posix/malloc/arch-specific.cpp +++ b/src/system/libroot/posix/malloc/arch-specific.cpp @@ -131,6 +131,13 @@ __init_heap_post_env(void) } +extern "C" void +__heap_terminate_after() +{ + // nothing to do +} + + static void insert_chunk(free_chunk *newChunk) { diff --git a/src/system/libroot/posix/malloc_debug/guarded_heap.cpp b/src/system/libroot/posix/malloc_debug/guarded_heap.cpp index 1a1b0a85c2..09793a478a 100644 --- a/src/system/libroot/posix/malloc_debug/guarded_heap.cpp +++ b/src/system/libroot/posix/malloc_debug/guarded_heap.cpp @@ -22,6 +22,7 @@ static const size_t kMaxStackTraceDepth = 50; static bool sDebuggerCalls = true; +static bool sDumpAllocationsOnExit = false; static size_t sStackTraceDepth = 0; #if __cplusplus >= 201103L @@ -50,6 +51,22 @@ panic(const char* format, ...) } +static void +print_stdout(const char* format, ...) +{ + // To avoid any allocations due to dynamic memory need by printf() we use a + // stack buffer and vsnprintf(). Otherwise this does the same as printf(). + char buffer[1024]; + + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + + write(STDOUT_FILENO, buffer, strlen(buffer)); +} + + // #pragma mark - Linked List @@ -218,12 +235,12 @@ guarded_heap_print_stack_trace(addr_t stackTrace[], size_t depth) status_t status = _kern_lookup_symbol(address, &baseAddress, symbolName, sizeof(symbolName), imageName, sizeof(imageName), &exactMatch); if (status != B_OK) { - printf("\t%#" B_PRIxADDR " (lookup failed: %s)\n", address, + print_stdout("\t%#" B_PRIxADDR " (lookup failed: %s)\n", address, strerror(status)); continue; } - printf("\t<%s> %s + %#" B_PRIxADDR "%s\n", imageName, symbolName, + print_stdout("\t<%s> %s + %#" B_PRIxADDR "%s\n", imageName, symbolName, address - baseAddress, (exactMatch ? "" : " (nearest)")); } } @@ -929,7 +946,7 @@ dump_allocations(guarded_heap& heap, bool statsOnly, thread_id thread) if (statsOnly) continue; - printf("allocation: base: %p; size: %" B_PRIuSIZE + print_stdout("allocation: base: %p; size: %" B_PRIuSIZE "; thread: %" B_PRId32 "; alignment: %" B_PRIuSIZE "\n", page.allocation_base, page.allocation_size, page.thread, page.alignment); @@ -939,11 +956,18 @@ dump_allocations(guarded_heap& heap, bool statsOnly, thread_id thread) } } - printf("total allocations: %" B_PRIuSIZE ", %" B_PRIuSIZE " bytes\n", + print_stdout("total allocations: %" B_PRIuSIZE ", %" B_PRIuSIZE " bytes\n", allocationCount, allocationSize); } +static void +dump_allocations_full() +{ + dump_allocations(sGuardedHeap, false, -1); +} + + // #pragma mark - Heap Debug API @@ -1047,6 +1071,14 @@ heap_debug_get_allocation_info(void *address, size_t *size, } +extern "C" status_t +heap_debug_dump_allocations_on_exit(bool enabled) +{ + sDumpAllocationsOnExit = enabled; + return B_OK; +} + + extern "C" status_t heap_debug_set_stack_trace_depth(size_t stackTraceDepth) { @@ -1104,6 +1136,8 @@ __init_heap_post_env(void) if (mode != NULL) { if (strchr(mode, 'r')) heap_debug_set_memory_reuse(false); + if (strchr(mode, 'e')) + heap_debug_dump_allocations_on_exit(true); size_t defaultAlignment = 0; const char *argument = strchr(mode, 'a'); @@ -1123,6 +1157,14 @@ __init_heap_post_env(void) } +extern "C" void +__heap_terminate_after() +{ + if (sDumpAllocationsOnExit) + dump_allocations_full(); +} + + // #pragma mark - Public API diff --git a/src/system/libroot/posix/malloc_debug/heap.cpp b/src/system/libroot/posix/malloc_debug/heap.cpp index 1da238e0d6..280953b2cc 100644 --- a/src/system/libroot/posix/malloc_debug/heap.cpp +++ b/src/system/libroot/posix/malloc_debug/heap.cpp @@ -1807,6 +1807,13 @@ heap_debug_get_allocation_info(void *address, size_t *size, } +extern "C" status_t +heap_debug_dump_allocations_on_exit(bool enabled) +{ + return B_NOT_SUPPORTED; +} + + extern "C" status_t heap_debug_set_stack_trace_depth(size_t stackTraceDepth) { @@ -1873,6 +1880,13 @@ __init_heap_post_env(void) } +extern "C" void +__heap_terminate_after() +{ + // nothing to do +} + + // #pragma mark - Public API