HeapTracker/heap_tracker_lib.c (218 lines of code) (raw):

/* * Copyright (c) Microsoft Corporation. * Licensed under the MIT License. */ #include <stdlib.h> #include <string.h> #include <stdbool.h> #include <pthread.h> #include "heap_tracker_lib.h" #include "applibs_versions.h" #include <applibs/log.h> ////////////////////////////////////////////////////////////////////////////////// // GLOBAL VARIABLES ////////////////////////////////////////////////////////////////////////////////// // Sets a reference allocation threshold (in bytes) after which the library will log warnings. const size_t heap_threshold = 250 * 1024; // Heap allocated amount (in bytes). This is signed so the user can debug allocation issues. volatile ssize_t heap_allocated = 0; int heap_track_pointer(void *ptr, size_t size); int heap_untrack_pointer(void *ptr); ////////////////////////////////////////////////////////////////////////////////// // THREAD SAFETY ////////////////////////////////////////////////////////////////////////////////// #if ENABLE_THREAD_SAFETY static pthread_mutex_t mux; #endif void heap_track_init(void) { #if ENABLE_THREAD_SAFETY pthread_mutex_init(&mux, NULL); #endif } #if ENABLE_THREAD_SAFETY # define MUTEX_LOCK pthread_mutex_lock(&mux); # define MUTEX_UNLOCK pthread_mutex_unlock(&mux); #else # define MUTEX_LOCK # define MUTEX_UNLOCK #endif ////////////////////////////////////////////////////////////////////////////////// // LOGGING ////////////////////////////////////////////////////////////////////////////////// #define HEAP_TRACKER_LIB_LOG_PREFIX "Heap-Tracker: " #define HeapTracker_Log(...) Log_Debug(HEAP_TRACKER_LIB_LOG_PREFIX __VA_ARGS__) #define LogHeapStatus() log_heap_status() void log_heap_status(void) { if (heap_allocated < 0) { Log_Debug("WARNING: heap_allocated (%zd) is NEGATIVE --> 'heap_allocated' will not be reliable from now on!\n", heap_allocated); } else if (heap_allocated > heap_threshold) { Log_Debug("WARNING: heap_allocated (%zd bytes) is above heap_threshold (%zu bytes)\n", heap_allocated, heap_threshold); } #if ENABLE_DEBUG_VERBOSE_LOGS else { Log_Debug("SUCCESS: heap_allocated (%zd bytes) - delta with heap_threshold(%zd bytes)\n", heap_allocated, (ssize_t)heap_threshold - heap_allocated); } #endif } ////////////////////////////////////////////////////////////////////////////////// // NATIVE malloc/free WRAPPERS ////////////////////////////////////////////////////////////////////////////////// /* * NOTE: the library does not intentionally alter the behaviors of the native functions, * in order to not disrupt the functionality on other system libraries that use them. * Altering the implementations is NOT reccomended as it could result in unpredicted App behavior! */ // C-Library malloc/free stubs, which will be inter-positioned by the wrappers at link-time void *__real_malloc(size_t size); void *__real_realloc(void *ptr, size_t new_size); void *__real_calloc(size_t num, size_t size); void *__real_aligned_alloc(size_t alignment, size_t size); void __real_free(void *ptr); // Heap-tracking malloc() wrapper void *__wrap_malloc(size_t size) { MUTEX_LOCK; void *ptr = __real_malloc(size); HeapTracker_Log("malloc(%zu)=%p... ", size, ptr); if (NULL != ptr) { heap_allocated += (ssize_t)size; #if ENABLE_POINTER_TRACKING heap_track_pointer(ptr, size); #endif // ENABLE_POINTER_TRACKING } LogHeapStatus(); MUTEX_UNLOCK; return ptr; } // Custom heap-tracking calloc() wrapper void *__wrap_calloc(size_t num, size_t size) { MUTEX_LOCK; void *ptr = __real_calloc(num, size); HeapTracker_Log("calloc(%zu,%zu)=%p...", num, size, ptr); if (ptr) { heap_allocated += (ssize_t)(num * size); #if ENABLE_POINTER_TRACKING heap_track_pointer(ptr, size); #endif // ENABLE_POINTER_TRACKING } LogHeapStatus(); MUTEX_UNLOCK; return ptr; } // Custom heap-tracking aligned_alloc() wrapper void *__wrap_aligned_alloc(size_t alignment, size_t size) { MUTEX_LOCK; void *ptr = __real_aligned_alloc(alignment, size); HeapTracker_Log("aligned_alloc(%zu,%zu)=%p...", alignment, size, ptr); if (ptr) { heap_allocated += (ssize_t)size; #if ENABLE_POINTER_TRACKING heap_track_pointer(ptr, size); #endif // ENABLE_POINTER_TRACKING } LogHeapStatus(); MUTEX_UNLOCK; return ptr; } // Custom heap-tracking realloc() wrapper void *__wrap_realloc(void *ptr, size_t new_size) { MUTEX_LOCK; void *new_ptr = __real_realloc(ptr, new_size); HeapTracker_Log("realloc(%p, %zu)=%p... ", ptr, new_size, new_ptr); if (NULL != new_ptr) { heap_allocated += (ssize_t)(new_size); #if ENABLE_POINTER_TRACKING if (ptr && -1 == heap_untrack_pointer(ptr)) { HeapTracker_Log("WARNING: free(%p) was called for a non-tracked pointer.\n", ptr); } if (-1 == heap_track_pointer(new_ptr, new_size)) { HeapTracker_Log("WARNING: free(%p) was called for a non-tracked pointer.\n", ptr); } #else HeapTracker_Log("WARNING! Native realloc(%p,%zu) was called instead of _realloc() helper: 'heap_allocated' will not be reliable from now on!\n", ptr, new_size); #endif } LogHeapStatus(); MUTEX_UNLOCK; return new_ptr; } // Native free() wrapper (does NOT track heap!) void __wrap_free(void *ptr) { MUTEX_LOCK; HeapTracker_Log("free(%p)... ", ptr); #if ENABLE_POINTER_TRACKING if (ptr && -1 == heap_untrack_pointer(ptr)) { HeapTracker_Log("WARNING: free(%p) was called for a non-tracked pointer.\n", ptr); } #else HeapTracker_Log("WARNING! Native free(%p) was called instead of _free() helper: 'heap_allocated' will not be reliable from now on!\n", ptr); #endif // ENABLE_POINTER_TRACKING LogHeapStatus(); __real_free(ptr); MUTEX_UNLOCK; } #if !ENABLE_POINTER_TRACKING // Custom heap-tracking free() helper void _free(void *ptr, size_t size) { MUTEX_LOCK; HeapTracker_Log("_free(%p,%zu)... ", ptr, size); __real_free(ptr); if (ptr) { heap_allocated -= (ssize_t)size; } LogHeapStatus(); MUTEX_UNLOCK; } // Custom heap-tracking realloc() helper void *_realloc(void *ptr, size_t old_size, size_t new_size) { MUTEX_LOCK; void *new_ptr = __real_realloc(ptr, new_size); HeapTracker_Log("_realloc(%p,%zu,%zu)=%p... ", ptr, old_size, new_size, new_ptr); if (NULL != new_ptr) { heap_allocated += (ssize_t)(new_size - old_size + 1); } LogHeapStatus(); MUTEX_UNLOCK; return new_ptr; } #endif // ENABLE_POINTER_TRACKING ////////////////////////////////////////////////////////////////////////////////// // POINTER TRACKING ////////////////////////////////////////////////////////////////////////////////// #if ENABLE_POINTER_TRACKING typedef struct { void *address; size_t size; } t_pointer; static t_pointer **allocated_pointers = NULL; static size_t allocated_pointers_size = 0; static unsigned int allocated_pointers_count = 0; int heap_track_pointer(void *ptr, size_t size) { if (NULL == allocated_pointers || allocated_pointers_size < (allocated_pointers_count * sizeof(void *))) { size_t new_size = (allocated_pointers_count + POINTER_TRACK_INC) * sizeof(void *); void *new_ptr = __real_realloc(allocated_pointers, new_size); if (NULL == new_ptr) { HeapTracker_Log("heap_track_pointer(%p,%zu) FAILED - out of memory!!", ptr, size); return -1; } else { allocated_pointers = new_ptr; allocated_pointers_size = new_size; } } allocated_pointers[allocated_pointers_count]->address = ptr; allocated_pointers[allocated_pointers_count]->size = size; allocated_pointers_count++; return 0; } int heap_untrack_pointer(void *ptr) { int pos = 0; t_pointer **cur = allocated_pointers; while (cur && pos < allocated_pointers_count) { if ((*cur)->address == ptr) { heap_allocated -= (ssize_t)(allocated_pointers[pos]->size); memcpy(allocated_pointers[pos], allocated_pointers[pos + 1], allocated_pointers_size - sizeof(t_pointer)); memset(allocated_pointers[allocated_pointers_count - 1], 0, sizeof(t_pointer)); allocated_pointers_count--; return 0; } cur++, pos++; } return -1; } #endif // ENABLE_POINTER_TRACKING