diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index ee5c1b3ca4..18ae0c5f8c 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -277,6 +277,11 @@ PSEUDOMODULES += lora ## PSEUDOMODULES += libc_gettimeofday +## @defgroup pseudomodule_malloc_tracing malloc_tracing +## @brief Debug dynamic memory management by hooking in a print into each call +## of malloc(), calloc(), realloc() and free +PSEUDOMODULES += malloc_tracing + ## @defgroup pseudomodule_mpu_stack_guard mpu_stack_guard ## @brief MPU based stack guard ## diff --git a/sys/malloc_thread_safe/Kconfig b/sys/malloc_thread_safe/Kconfig index d50748ca75..2486e205ae 100644 --- a/sys/malloc_thread_safe/Kconfig +++ b/sys/malloc_thread_safe/Kconfig @@ -1,4 +1,4 @@ -# Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg +# Copyright (C) 2020, 2022 Otto-von-Guericke-Universität Magdeburg # # This file is subject to the terms and conditions of the GNU Lesser # General Public License v2.1. See the file LICENSE in the top level @@ -16,3 +16,18 @@ config MODULE_MALLOC_THREAD_SAFE safe without touching the application code or the c library. This module is intended to be pulled in automatically if needed. Hence, applications never should manually use it. + +config MODULE_MALLOC_TRACING + bool + depends on TEST_KCONFIG + depends on MODULE_MALLOC_THREAD_SAFE + help + This module enables hooks in the wrappers for malloc(), calloc(), + realloc(), and free() provided by MODULE_MALLOC_THREAD_SAFE that print + the arguments, caller program counter and return value of those + functions. The intent is to aid debugging invalid calls to free(), + duplicated calls to free(), or memory leaks. + + Note that generally dynamic memory management is a bad idea on the + constrained devices RIOT is targeting. So maybe it is better to just + adapt your code to use static memory management instead. diff --git a/sys/malloc_thread_safe/malloc_wrappers.c b/sys/malloc_thread_safe/malloc_wrappers.c index 6eed832b4a..cf226cc402 100644 --- a/sys/malloc_thread_safe/malloc_wrappers.c +++ b/sys/malloc_thread_safe/malloc_wrappers.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2019 Gunar Schorcht + * 2022 Otto-von-Guericke-Universität Magdeburg * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -12,12 +13,17 @@ * @file * @brief Implements various POSIX syscalls * @author Gunar Schorcht + * @author Marian Buschsieweke */ +#include #include +#include "architecture.h" #include "assert.h" +#include "cpu.h" #include "irq.h" +#include "kernel_defines.h" #include "mutex.h" extern void *__real_malloc(size_t size); @@ -28,15 +34,27 @@ static mutex_t _lock; void __attribute__((used)) *__wrap_malloc(size_t size) { + uinttxtptr_t pc; + if (IS_USED(MODULE_MALLOC_TRACING)) { + pc = cpu_get_caller_pc(); + } assert(!irq_is_in()); mutex_lock(&_lock); void *ptr = __real_malloc(size); mutex_unlock(&_lock); + if (IS_USED(MODULE_MALLOC_TRACING)) { + printf("malloc(%u) @ 0x%" PRIxTXTPTR " returned %p\n", + (unsigned)size, pc, ptr); + } return ptr; } void __attribute__((used)) __wrap_free(void *ptr) { + if (IS_USED(MODULE_MALLOC_TRACING)) { + uinttxtptr_t pc = cpu_get_caller_pc(); + printf("free(%p) @0x%" PRIxTXTPTR ")\n", ptr, pc); + } assert(!irq_is_in()); mutex_lock(&_lock); __real_free(ptr); @@ -45,28 +63,53 @@ void __attribute__((used)) __wrap_free(void *ptr) void * __attribute__((used)) __wrap_calloc(size_t nmemb, size_t size) { + uinttxtptr_t pc; + if (IS_USED(MODULE_MALLOC_TRACING)) { + pc = cpu_get_caller_pc(); + } /* some c libs don't perform proper overflow check (e.g. newlib < 4.0.0). Hence, we * just implement calloc on top of malloc ourselves. In addition to ensuring proper * overflow checks, this likely saves a bit of ROM */ size_t total_size; if (__builtin_mul_overflow(nmemb, size, &total_size)) { + if (IS_USED(MODULE_MALLOC_TRACING)) { + printf("calloc(%u, %u) @0x%" PRIxTXTPTR " overflowed\n", + (unsigned)nmemb, (unsigned)size, pc); + } return NULL; } - void *res = __wrap_malloc(total_size); + mutex_lock(&_lock); + void *res = __real_malloc(total_size); + mutex_unlock(&_lock); if (res) { memset(res, 0, total_size); } + if (IS_USED(MODULE_MALLOC_TRACING)) { + printf("calloc(%u, %u) @0x%" PRIxTXTPTR " returned %p\n", + (unsigned)nmemb, (unsigned)size, pc, res); + } + return res; } void * __attribute__((used))__wrap_realloc(void *ptr, size_t size) { + uinttxtptr_t pc; + if (IS_USED(MODULE_MALLOC_TRACING)) { + pc = cpu_get_caller_pc(); + } + assert(!irq_is_in()); mutex_lock(&_lock); void *new = __real_realloc(ptr, size); mutex_unlock(&_lock); + + if (IS_USED(MODULE_MALLOC_TRACING)) { + printf("realloc(%p, %u) @0x%" PRIxTXTPTR " returned %p\n", + ptr, (unsigned)size, pc, new); + } return new; }