diff --git a/include/os_malloc.h b/include/os_malloc.h index ef938c2..8bc2d36 100644 --- a/include/os_malloc.h +++ b/include/os_malloc.h @@ -9,6 +9,7 @@ #define OS_MALLOC_H #include +#include #include #include "attribs.h" @@ -16,6 +17,8 @@ extern "C" { #endif +#define OS_MALLOC_TRACE 0 + /** * This is a wrap for malloc - it allocates a block of memory of size 'size' bytes. * @param size - the size the memory block @@ -23,8 +26,14 @@ extern "C" { */ ATTR_MALLOC ATTR_MALLOC_SIZE(1) +#if OS_MALLOC_TRACE +void* +os_malloc_internal(const size_t size, const char* const p_file, const int32_t line); +#define os_malloc(size) os_malloc_internal(size, __FILE__, __LINE__) +#else void* os_malloc(const size_t size); +#endif /** * This is a wrap for calloc - it allocates memory block for an array of 'nmemb' elements, @@ -35,8 +44,14 @@ os_malloc(const size_t size); */ ATTR_MALLOC ATTR_CALLOC_SIZE(1, 2) +#if OS_MALLOC_TRACE +void* +os_calloc_internal(const size_t nmemb, const size_t size, const char* const p_file, const int32_t line); +#define os_calloc(nmemb, size) os_calloc_internal(nmemb, size, __FILE__, __LINE__) +#else void* os_calloc(const size_t nmemb, const size_t size); +#endif /** * @brief This is a safer wrap for realloc, @@ -49,8 +64,14 @@ os_calloc(const size_t nmemb, const size_t size); * @return true if the reallocation was successful. */ ATTR_WARN_UNUSED_RESULT +#if OS_MALLOC_TRACE +bool +os_realloc_safe_internal(void** const p_ptr, const size_t size, const char* const p_file, const int32_t line); +#define os_realloc_safe(p_ptr, size) os_realloc_safe_internal(p_ptr, size, __FILE__, __LINE__) +#else bool os_realloc_safe(void** const p_ptr, const size_t size); +#endif /** * @brief This is a safer wrap for realloc, @@ -65,26 +86,51 @@ os_realloc_safe(void** const p_ptr, const size_t size); */ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) +#if OS_MALLOC_TRACE +bool +os_realloc_safe_and_clean_internal(void** const p_ptr, const size_t size, const char* const p_file, const int32_t line); +#define os_realloc_safe_and_clean(p_ptr, size) os_realloc_safe_and_clean_internal(p_ptr, size, __FILE__, __LINE__) +#else bool os_realloc_safe_and_clean(void** const p_ptr, const size_t size); +#endif /** * This is a wrap for 'free' - it deallocates a block of memory * @note This function should not be used, use macro @ref os_free instead. * @param ptr - pointer to the memory block */ +#if OS_MALLOC_TRACE +void +os_free_internal(void* ptr, const char* const p_file, const int32_t line); +#else void os_free_internal(void* ptr); +#endif /** * @brief os_free - is a wrap for 'free' which automatically sets pointer to NULL after the memory freeing. */ +#if OS_MALLOC_TRACE +#define os_free(ptr) \ + do \ + { \ + os_free_internal((void*)(ptr), __FILE__, __LINE__); \ + ptr = NULL; \ + } while (0) +#else #define os_free(ptr) \ do \ { \ os_free_internal((void*)(ptr)); \ ptr = NULL; \ } while (0) +#endif + +#if OS_MALLOC_TRACE +void +os_malloc_trace_dump(void); +#endif #ifdef __cplusplus } diff --git a/src/os_malloc.c b/src/os_malloc.c index 3932108..a6d7d77 100644 --- a/src/os_malloc.c +++ b/src/os_malloc.c @@ -8,6 +8,61 @@ #include "os_malloc.h" #include +#if OS_MALLOC_TRACE +#include +#include "sys/queue.h" +#include "os_mutex.h" +#define LOG_LOCAL_LEVEL LOG_LEVEL_INFO +#include "log.h" +static const char* TAG = "MEM_TRACE"; + +typedef struct os_malloc_trace_info_t +{ + void* p_mem; + size_t size; + TAILQ_ENTRY(os_malloc_trace_info_t) list; + const char* p_file; + int32_t line; + uint32_t timestamp; +} os_malloc_trace_info_t; + +typedef TAILQ_HEAD(os_malloc_trace_list_t, os_malloc_trace_info_t) os_malloc_trace_list_t; + +static os_malloc_trace_list_t g_os_malloc_trace_list; +static os_mutex_t g_p_os_malloc_trace_mutex; +static os_mutex_static_t g_os_malloc_trace_mutex_mem; +static int32_t g_os_malloc_trace_cnt; + +static os_malloc_trace_list_t* +os_malloc_trace_mutex_lock(void) +{ + if (NULL == g_p_os_malloc_trace_mutex) + { + g_p_os_malloc_trace_mutex = os_mutex_create_static(&g_os_malloc_trace_mutex_mem); + TAILQ_INIT(&g_os_malloc_trace_list); + g_os_malloc_trace_cnt = 0; + } + os_mutex_lock(g_p_os_malloc_trace_mutex); + return &g_os_malloc_trace_list; +} + +static void +os_malloc_trace_mutex_unlock(os_malloc_trace_list_t** p_p_list) +{ + os_mutex_unlock(g_p_os_malloc_trace_mutex); + *p_p_list = NULL; +} +#endif + +#if OS_MALLOC_TRACE +ATTR_MALLOC +ATTR_MALLOC_SIZE(1) +void* +os_malloc_internal(const size_t size, const char* const p_file, const int32_t line) +{ + return os_calloc_internal(1, size, p_file, line); +} +#else ATTR_MALLOC ATTR_MALLOC_SIZE(1) void* @@ -15,7 +70,28 @@ os_malloc(const size_t size) { return malloc(size); } +#endif + +#if OS_MALLOC_TRACE +void +os_free_internal(void* ptr, const char* const p_file, const int32_t line) +{ + if (NULL == ptr) + { + return; + } + + g_os_malloc_trace_cnt -= 1; + + os_malloc_trace_info_t* const p_info = (os_malloc_trace_info_t*)((uint8_t*)ptr - sizeof(os_malloc_trace_info_t)); + os_malloc_trace_list_t* p_list = os_malloc_trace_mutex_lock(); + TAILQ_REMOVE(p_list, p_info, list); + os_malloc_trace_mutex_unlock(&p_list); + memset(p_info, 0, sizeof(*p_info) + p_info->size); + free(p_info); +} +#else void os_free_internal(void* ptr) { @@ -24,7 +100,35 @@ os_free_internal(void* ptr) free(ptr); } } +#endif +#if OS_MALLOC_TRACE +ATTR_MALLOC +ATTR_CALLOC_SIZE(1, 2) +void* +os_calloc_internal(const size_t nmemb, const size_t size, const char* const p_file, const int32_t line) +{ + void* const p_mem = calloc(1, sizeof(os_malloc_trace_info_t) + nmemb * size); + if (NULL == p_mem) + { + return NULL; + } + + os_malloc_trace_info_t* const p_info = p_mem; + p_info->p_mem = p_mem; + p_info->size = size; + p_info->p_file = p_file; + p_info->line = line; + p_info->timestamp = xTaskGetTickCount(); + + g_os_malloc_trace_cnt += 1; + + os_malloc_trace_list_t* p_list = os_malloc_trace_mutex_lock(); + TAILQ_INSERT_TAIL(p_list, p_info, list); + os_malloc_trace_mutex_unlock(&p_list); + return (void*)((uint8_t*)p_mem + sizeof(os_malloc_trace_info_t)); +} +#else ATTR_MALLOC ATTR_CALLOC_SIZE(1, 2) void* @@ -32,7 +136,35 @@ os_calloc(const size_t nmemb, const size_t size) { return calloc(nmemb, size); } +#endif + +#if OS_MALLOC_TRACE +ATTR_WARN_UNUSED_RESULT +ATTR_NONNULL(1) +bool +os_realloc_safe_internal(void** const p_ptr, const size_t size, const char* const p_file, const int32_t line) +{ + void* ptr = *p_ptr; + if (NULL == ptr) + { + return false; + } + + const os_malloc_trace_info_t* const p_info_old + = (os_malloc_trace_info_t*)((uint8_t*)ptr - sizeof(os_malloc_trace_info_t)); + void* p_new_ptr = os_malloc_internal(size, p_file, line); + if (NULL == p_new_ptr) + { + *p_ptr = NULL; + return false; + } + memcpy(p_new_ptr, ptr, ((size < p_info_old->size) ? size : p_info_old->size)); + os_free_internal(ptr, p_file, line); + *p_ptr = p_new_ptr; + return true; +} +#else ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) bool @@ -47,7 +179,23 @@ os_realloc_safe(void** const p_ptr, const size_t size) *p_ptr = p_new_ptr; return true; } +#endif +#if OS_MALLOC_TRACE +ATTR_WARN_UNUSED_RESULT +ATTR_NONNULL(1) +bool +os_realloc_safe_and_clean_internal(void** const p_ptr, const size_t size, const char* const p_file, const int32_t line) +{ + void* p_old_ptr = *p_ptr; + const bool res = os_realloc_safe_internal(p_ptr, size, p_file, line); + if (!res) + { + os_free_internal(p_old_ptr, p_file, line); + } + return res; +} +#else ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) bool @@ -63,3 +211,28 @@ os_realloc_safe_and_clean(void** const p_ptr, const size_t size) *p_ptr = p_new_ptr; return true; } +#endif + +#if OS_MALLOC_TRACE +void +os_malloc_trace_dump(void) +{ + os_malloc_trace_list_t* p_list = os_malloc_trace_mutex_lock(); + uint32_t cnt = 0; + LOG_INFO("Num blocks allocated: %d", g_os_malloc_trace_cnt); + os_malloc_trace_info_t* p_info; + TAILQ_FOREACH(p_info, p_list, list) + { + LOG_INFO( + "[%4u] %p: %u bytes (at %u), %s:%d", + cnt, + p_info->p_mem, + p_info->size, + p_info->timestamp, + p_info->p_file, + p_info->line); + cnt += 1; + } + os_malloc_trace_mutex_unlock(&p_list); +} +#endif