From f31f82042a5310e3f5b23fdb9c4e940edff56436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikolai=20G=C3=BCtschow?= Date: Wed, 24 Jan 2024 13:31:54 +0100 Subject: [PATCH] sys: add malloc_monitor, deprecate malloc_tracing --- makefiles/pseudomodules.inc.mk | 4 + sys/Kconfig | 1 + sys/include/malloc_monitor.h | 59 +++++++ sys/include/malloc_monitor_internal.h | 73 ++++++++ sys/malloc_monitor/Kconfig | 23 +++ sys/malloc_monitor/Makefile | 1 + sys/malloc_monitor/Makefile.dep | 1 + sys/malloc_monitor/doc.txt | 72 ++++++++ sys/malloc_monitor/malloc_monitor.c | 170 ++++++++++++++++++ sys/malloc_thread_safe/Kconfig | 2 +- sys/malloc_thread_safe/doc.txt | 2 +- sys/malloc_thread_safe/malloc_wrappers.c | 13 ++ tests/sys/malloc_monitor/Makefile | 7 + tests/sys/malloc_monitor/Makefile.ci | 4 + tests/sys/malloc_monitor/main.c | 212 +++++++++++++++++++++++ tests/sys/malloc_monitor/tests/01-run.py | 14 ++ 16 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 sys/include/malloc_monitor.h create mode 100644 sys/include/malloc_monitor_internal.h create mode 100644 sys/malloc_monitor/Kconfig create mode 100644 sys/malloc_monitor/Makefile create mode 100644 sys/malloc_monitor/Makefile.dep create mode 100644 sys/malloc_monitor/doc.txt create mode 100644 sys/malloc_monitor/malloc_monitor.c create mode 100644 tests/sys/malloc_monitor/Makefile create mode 100644 tests/sys/malloc_monitor/Makefile.ci create mode 100644 tests/sys/malloc_monitor/main.c create mode 100755 tests/sys/malloc_monitor/tests/01-run.py diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index d67d9c9449..7e8989e7e6 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -259,7 +259,11 @@ 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 +## @{ +## @deprecated Use module `malloc_monitor` with verbous configuration instead; +## will be removed after 2024.07 release. PSEUDOMODULES += malloc_tracing +## @} ## @defgroup pseudomodule_mpu_stack_guard mpu_stack_guard ## @brief MPU based stack guard diff --git a/sys/Kconfig b/sys/Kconfig index 97ed70068a..ec7297e1b3 100644 --- a/sys/Kconfig +++ b/sys/Kconfig @@ -79,6 +79,7 @@ rsource "evtimer/Kconfig" rsource "log_color/Kconfig" rsource "log_printfnoformat/Kconfig" rsource "luid/Kconfig" +rsource "malloc_monitor/Kconfig" rsource "malloc_thread_safe/Kconfig" rsource "matstat/Kconfig" rsource "memarray/Kconfig" diff --git a/sys/include/malloc_monitor.h b/sys/include/malloc_monitor.h new file mode 100644 index 0000000000..28c9117ea9 --- /dev/null +++ b/sys/include/malloc_monitor.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 TU Dresden + * + * 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 + * directory for more details. + */ +/** + * @ingroup sys_malloc_monitor + * @{ + */ + +#ifndef MALLOC_MONITOR_H +#define MALLOC_MONITOR_H + +#include +#include +#include +#include + +#include "architecture.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Obtain current heap memory usage. + * + * @return current heap memory usage in bytes + */ +size_t malloc_monitor_get_usage_current(void); + +/** + * @brief Obtain maximum heap memory usage since last call to + * @ref malloc_monitor_reset_high_watermark(). + * + * @return maximum heap memory usage in bytes + */ +size_t malloc_monitor_get_usage_high_watermark(void); + +/** + * @brief Reset maximum heap memory usage. + * + * After calling this function, @ref malloc_monitor_get_usage_high_watermark() + * will return @ref malloc_monitor_get_usage_current() until further changes + * to heap memory usage. + */ +void malloc_monitor_reset_high_watermark(void); + +#ifdef __cplusplus +} +#endif + +#endif /* MALLOC_MONITOR_H */ + +/** + * @} + */ diff --git a/sys/include/malloc_monitor_internal.h b/sys/include/malloc_monitor_internal.h new file mode 100644 index 0000000000..1a123cb38e --- /dev/null +++ b/sys/include/malloc_monitor_internal.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 TU Dresden + * + * 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 + * directory for more details. + */ +/** + * @defgroup sys_malloc_monitor_internals Heap Memory Usage Monitor internals + * @ingroup sys_malloc_monitor + * @{ + * + * @brief internals for monitoring heap memory usage (calls to malloc/calloc/realloc/free) + * @author Mikolai Gütschow + */ + +#ifndef MALLOC_MONITOR_INTERNAL_H +#define MALLOC_MONITOR_INTERNAL_H + +#include +#include +#include +#include + +#include "architecture.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Record malloc/calloc/realloc call increasing heap usage. + * + * @param[in] ptr pointer to newly allocated memory + * @param[in] size size of newly allocated memory + * @param[in] pc PC of calling function + * @param[in] func_prefix prefix identifying memory function, one of "m","c","re" + * + * @internal + */ +void malloc_monitor_add(void *ptr, size_t size, uinttxtptr_t pc, char *func_prefix); + +/** + * @brief Record free/realloc call decreasing heap usage. + * + * @param[in] ptr pointer to memory that is being freed + * @param[in] pc PC of calling function + * + * @internal + */ +void malloc_monitor_rm(void *ptr, uinttxtptr_t pc); + +/** + * @brief Record realloc call either increasing or decreasing heap usage. + * + * @param[in] ptr_old pointer to previously allocated memory + * @param[in] ptr_new pointer to newly allocated memory + * @param[in] size_new size of newly allocated memory + * @param[in] pc PC of calling function + * + * @internal + */ +void malloc_monitor_mv(void *ptr_old, void *ptr_new, size_t size_new, uinttxtptr_t pc); + +#ifdef __cplusplus +} +#endif + +#endif /* MALLOC_MONITOR_INTERNAL_H */ + +/** + * @} + */ diff --git a/sys/malloc_monitor/Kconfig b/sys/malloc_monitor/Kconfig new file mode 100644 index 0000000000..73aad813d4 --- /dev/null +++ b/sys/malloc_monitor/Kconfig @@ -0,0 +1,23 @@ +# Copyright (C) 2024 TU Dresden +# +# 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 +# directory for more details. +# + +menuconfig MODULE_SYS_MALLOC_MONITOR + bool "Heap Memory Usage Monitor" + +config MODULE_SYS_MALLOC_MONITOR_SIZE + int "Monitor Size" + default 100 + depends on MODULE_SYS_MALLOC_MONITOR + help + Specifies maximum number of pointers that can be monitored at once. + +config MODULE_SYS_MALLOC_MONITOR_VERBOSE + bool "Verbose" + default false + depends on MODULE_SYS_MALLOC_MONITOR + help + Print detailed log of calls to malloc/realloc/free diff --git a/sys/malloc_monitor/Makefile b/sys/malloc_monitor/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/malloc_monitor/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/malloc_monitor/Makefile.dep b/sys/malloc_monitor/Makefile.dep new file mode 100644 index 0000000000..29729aa0a4 --- /dev/null +++ b/sys/malloc_monitor/Makefile.dep @@ -0,0 +1 @@ +USEMODULE += malloc_thread_safe diff --git a/sys/malloc_monitor/doc.txt b/sys/malloc_monitor/doc.txt new file mode 100644 index 0000000000..f3c00a3c0a --- /dev/null +++ b/sys/malloc_monitor/doc.txt @@ -0,0 +1,72 @@ +/** + * @defgroup sys_malloc_monitor Heap Memory Usage Monitor + * @ingroup sys_memory_management + * @brief This module allows to monitor the dynamic memory usage of a certain piece of code. + * @warning This module automatically selects @ref sys_malloc_ts and naturally + * incurs a certain runtime overhead. It is not meant for production usage. + * @author Mikolai Gütschow + * + * # Description + * + * This module allows to monitor the dynamic memory usage of a certain piece of code. + * It works by hooking into (wrappers to) @ref malloc(), @ref calloc(), @ref realloc(), + * and @ref free() calls to internally record the current and all-time maximum heap memory usage. + * + * Note that in general 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. + * + * # Usage + * + * Enable the module with `USEMODULE += malloc_monitor`. + * + * Add `#include "malloc_monitor.h"` to the file in which you want to monitor dynamic memory usage. + * Use @ref malloc_monitor_get_usage_current() to retrieve the size of the currently allocated + * heap memory in bytes. @ref malloc_monitor_get_usage_high_watermark() returns the all-time maximum + * since startup or the last call to @ref malloc_monitor_reset_high_watermark(). + * + * Note that `malloc_monitor` currently has no notion of threads and will at any point in time report + * the global dynamic memory usage, not the one used by the currently running thread. + * Thread-safety is achieved through usage of @ref sys_malloc_ts, though. + * + * ## Example + * + * Imagine you want to investigate the dynamic memory consumption of a certain function `func()`. + * The following snippet could get you started: + * + * ```c + * #include + * #include + * + * #include "malloc_monitor.h" + * + * int main(void) + * { + * size_t before = malloc_monitor_get_usage_current(); + * size_t before_max = malloc_monitor_get_usage_high_watermark(); + * func(); + * size_t after = malloc_monitor_get_usage_current(); + * size_t after_max = malloc_monitor_get_usage_high_watermark(); + * + * if (after != before) { + * puts("func() " (after < before ? "decreased" : "increased") " global dynamic memory usage."); + * } + * printf("The maximal dynamic memory usage of func() was %d bytes.", after_max - before_max); + * } + * ``` + * + * For further usage examples, refer to the corresponding tests in `tests/sys/malloc_monitor`. + * + * # Configuration + * + * The maximum number of pointers that can be monitored at once can be set with Kconfig + * in System > Heap Memory Usage Monitor > Monitor Size or by setting the corresponding + * CFlag in your application's Makefile as `CFLAGS += CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE=42`. + * It defaults to 100. + * + * For more fine-grained debugging of invalid calls to @ref free(), duplicated calls to @ref free(), + * or memory leaks, the module can be configured to print information on every call to @ref malloc(), + * @ref calloc(), @ref realloc(), or @ref free() by setting System > Heap Memory Usage Monitor > Verbose + * or adding `CFLAGS += CONFIG_MODULE_SYS_MALLOC_MONITOR_VERBOSE=1` to your Makefile. + * `malloc_monitor` defaults to be non-verbose. + * + */ diff --git a/sys/malloc_monitor/malloc_monitor.c b/sys/malloc_monitor/malloc_monitor.c new file mode 100644 index 0000000000..ec05ffc54c --- /dev/null +++ b/sys/malloc_monitor/malloc_monitor.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2024 TU Dresden + * + * 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 + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief monitor heap memory usage (calls to malloc/realloc/free) + * @author Mikolai Gütschow + */ + +#include +#include + +#include "architecture.h" +#include "assert.h" +#include "cpu.h" +#include "irq.h" +#include "mutex.h" + +#include "malloc_monitor.h" +#include "malloc_monitor_internal.h" + +#ifndef CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE +#define CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE 100 +#endif + +#ifndef CONFIG_MODULE_SYS_MALLOC_MONITOR_VERBOSE +#define CONFIG_MODULE_SYS_MALLOC_MONITOR_VERBOSE 0 +#endif + +static struct { + void *addr[CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE]; + size_t size[CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE]; + size_t current; + size_t high_watermark; +} malloc_monitor = { + .addr = {NULL}, + .current = 0, + .high_watermark = 0, +}; + +/* guards access to malloc_monitor */ +static mutex_t _lock; + +void malloc_monitor_add(void *ptr, size_t size, uinttxtptr_t pc, char *func_prefix) +{ + if (ptr == NULL) { + return; + } +#if CONFIG_MODULE_SYS_MALLOC_MONITOR_VERBOSE + printf("%salloc(%" PRIuSIZE ") @ 0x%" PRIxTXTPTR " returned %p\n", + func_prefix, size, pc, ptr); +#endif + assert(!irq_is_in()); + mutex_lock(&_lock); + for (uint8_t i=0; i malloc_monitor.high_watermark) { + malloc_monitor.high_watermark = malloc_monitor.current; + } + mutex_unlock(&_lock); + return; + } + } + mutex_unlock(&_lock); + printf("malloc_monitor: maximum number of pointers to be monitored " + "(as set by CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE) exceeded.\n"); + (void)func_prefix; + (void)pc; +} + +void malloc_monitor_rm(void *ptr, uinttxtptr_t pc) +{ + if (ptr == NULL) { + return; + } +#if CONFIG_MODULE_SYS_MALLOC_MONITOR_VERBOSE + printf("malloc_monitor: free(%p) @ 0x%" PRIxTXTPTR " \n", ptr, pc); +#endif + assert(!irq_is_in()); + mutex_lock(&_lock); + for (uint8_t i=0; i size_old) { + malloc_monitor.current += size_new - size_old; + if (malloc_monitor.current > malloc_monitor.high_watermark) { + malloc_monitor.high_watermark = malloc_monitor.current; + } + } + else { + malloc_monitor.current -= size_old - size_new; + } + mutex_unlock(&_lock); + return; + } + } + mutex_unlock(&_lock); + printf("malloc_monitor: realloc(%p) @ 0x%" PRIxTXTPTR " invalid\n", ptr_old, pc); +} + +size_t malloc_monitor_get_usage_current(void) +{ + assert(!irq_is_in()); + mutex_lock(&_lock); + size_t ret = malloc_monitor.current; + mutex_unlock(&_lock); + return ret; +} + +size_t malloc_monitor_get_usage_high_watermark(void) +{ + assert(!irq_is_in()); + mutex_lock(&_lock); + size_t ret = malloc_monitor.high_watermark; + mutex_unlock(&_lock); + return ret; +} + +void malloc_monitor_reset_high_watermark(void) +{ + assert(!irq_is_in()); + mutex_lock(&_lock); + malloc_monitor.high_watermark = malloc_monitor.current; + mutex_unlock(&_lock); +} + +/** @} */ diff --git a/sys/malloc_thread_safe/Kconfig b/sys/malloc_thread_safe/Kconfig index 2486e205ae..d7ef706a39 100644 --- a/sys/malloc_thread_safe/Kconfig +++ b/sys/malloc_thread_safe/Kconfig @@ -30,4 +30,4 @@ config MODULE_MALLOC_TRACING 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. + adapt your code to use static memory management instead. diff --git a/sys/malloc_thread_safe/doc.txt b/sys/malloc_thread_safe/doc.txt index b5c98642b6..92929fd669 100644 --- a/sys/malloc_thread_safe/doc.txt +++ b/sys/malloc_thread_safe/doc.txt @@ -1,6 +1,6 @@ /** @defgroup sys_malloc_ts Thread-safe wrappers for malloc and friends -@ingroup sys +@ingroup sys_memory_management @brief This module provides wrappers for malloc, calloc, realloc and free that provide mutually exclusive access to those functions. @warning This module is automatically selected, if needed. Never add it diff --git a/sys/malloc_thread_safe/malloc_wrappers.c b/sys/malloc_thread_safe/malloc_wrappers.c index 487440056f..f88f577090 100644 --- a/sys/malloc_thread_safe/malloc_wrappers.c +++ b/sys/malloc_thread_safe/malloc_wrappers.c @@ -24,6 +24,7 @@ #include "cpu.h" #include "irq.h" #include "kernel_defines.h" +#include "malloc_monitor_internal.h" #include "mutex.h" extern void *__real_malloc(size_t size); @@ -41,6 +42,9 @@ void __attribute__((used)) *__wrap_malloc(size_t size) assert(!irq_is_in()); mutex_lock(&_lock); void *ptr = __real_malloc(size); + if (IS_USED(MODULE_MALLOC_MONITOR)) { + malloc_monitor_add(ptr, size, cpu_get_caller_pc(), "m"); + } mutex_unlock(&_lock); if (IS_USED(MODULE_MALLOC_TRACING)) { printf("malloc(%" PRIuSIZE ") @ 0x%" PRIxTXTPTR " returned %p\n", @@ -58,6 +62,9 @@ void __attribute__((used)) __wrap_free(void *ptr) assert(!irq_is_in()); mutex_lock(&_lock); __real_free(ptr); + if (IS_USED(MODULE_MALLOC_MONITOR)) { + malloc_monitor_rm(ptr, cpu_get_caller_pc()); + } mutex_unlock(&_lock); } @@ -81,6 +88,9 @@ void * __attribute__((used)) __wrap_calloc(size_t nmemb, size_t size) mutex_lock(&_lock); void *res = __real_malloc(total_size); + if (IS_USED(MODULE_MALLOC_MONITOR)) { + malloc_monitor_add(res, total_size, cpu_get_caller_pc(), "c"); + } mutex_unlock(&_lock); if (res) { memset(res, 0, total_size); @@ -104,6 +114,9 @@ void * __attribute__((used))__wrap_realloc(void *ptr, size_t size) assert(!irq_is_in()); mutex_lock(&_lock); void *new = __real_realloc(ptr, size); + if (IS_USED(MODULE_MALLOC_MONITOR)) { + malloc_monitor_mv(ptr, new, size, cpu_get_caller_pc()); + } mutex_unlock(&_lock); if (IS_USED(MODULE_MALLOC_TRACING)) { diff --git a/tests/sys/malloc_monitor/Makefile b/tests/sys/malloc_monitor/Makefile new file mode 100644 index 0000000000..ef34a8c649 --- /dev/null +++ b/tests/sys/malloc_monitor/Makefile @@ -0,0 +1,7 @@ +include ../Makefile.sys_common + +USEMODULE += embunit + +USEMODULE += malloc_monitor + +include $(RIOTBASE)/Makefile.include diff --git a/tests/sys/malloc_monitor/Makefile.ci b/tests/sys/malloc_monitor/Makefile.ci new file mode 100644 index 0000000000..363d4f3a79 --- /dev/null +++ b/tests/sys/malloc_monitor/Makefile.ci @@ -0,0 +1,4 @@ +BOARD_INSUFFICIENT_MEMORY := \ + atmega8 \ + nucleo-l011k4 \ + # diff --git a/tests/sys/malloc_monitor/main.c b/tests/sys/malloc_monitor/main.c new file mode 100644 index 0000000000..3b52332019 --- /dev/null +++ b/tests/sys/malloc_monitor/main.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2024 TU Dresden + * + * 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 + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for MODULE_MALLOC_MONITOR + * + * @author Mikolai Gütschow + * @} + */ + +#include +#include +#include + +#include "embUnit.h" + +#include "malloc_monitor.h" + +#define MALLOC_SIZE 1 + +#define TEST_MALLOC_MONITOR_SAVE \ + size_t curr = malloc_monitor_get_usage_current(); \ + size_t water = malloc_monitor_get_usage_high_watermark(); + +#define TEST_ASSERT_CURRENT(num_alloc) TEST_ASSERT_EQUAL_INT(curr+num_alloc*MALLOC_SIZE, malloc_monitor_get_usage_current()); +#define TEST_ASSERT_WATERMARK(num_alloc) TEST_ASSERT_EQUAL_INT(water+num_alloc*MALLOC_SIZE, malloc_monitor_get_usage_high_watermark()); + +/* + * malloc and free should be reflected by `malloc_monitor_get_usage_current()`. + * `malloc_monitor_get_usage_high_watermark()` should only be decreased on a call to + * `malloc_monitor_reset_high_watermark()` + */ +static void test_malloc_free(void) +{ + TEST_MALLOC_MONITOR_SAVE + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(0); + + void *volatile alloc1 = malloc(MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc1); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(1); + + void *volatile alloc2 = malloc(MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc2); + + TEST_ASSERT_CURRENT(2); + TEST_ASSERT_WATERMARK(2); + + free(alloc1); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(2); + + malloc_monitor_reset_high_watermark(); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(1); + + free(alloc2); + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(1); + + malloc_monitor_reset_high_watermark(); + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(0); +} +/* + * using calloc instead of malloc should be reflected correctly + */ +static void test_calloc(void) +{ + TEST_MALLOC_MONITOR_SAVE + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(0); + + void *volatile alloc1 = calloc(1, MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc1); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(1); + + void *volatile alloc2 = calloc(2, MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc2); + + TEST_ASSERT_CURRENT(3); + TEST_ASSERT_WATERMARK(3); + + free(alloc1); + + TEST_ASSERT_CURRENT(2); + TEST_ASSERT_WATERMARK(3); + + malloc_monitor_reset_high_watermark(); + + TEST_ASSERT_CURRENT(2); + TEST_ASSERT_WATERMARK(2); + + free(alloc2); + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(2); + + malloc_monitor_reset_high_watermark(); + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(0); +} + +/* + * using realloc instead of malloc/free should be reflected correctly + */ +static void test_realloc(void) +{ + TEST_MALLOC_MONITOR_SAVE + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(0); + + void *volatile alloc1 = realloc(NULL, MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc1); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(1); + + alloc1 = realloc(alloc1, 2*MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc1); + + TEST_ASSERT_CURRENT(2); + TEST_ASSERT_WATERMARK(2); + + alloc1 = realloc(alloc1, 1*MALLOC_SIZE); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(2); + + malloc_monitor_reset_high_watermark(); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(1); + + alloc1 = realloc(alloc1, 0*MALLOC_SIZE); + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(1); + + malloc_monitor_reset_high_watermark(); + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(0); +} + +/* + * freeing NULL shouldn't change anything + */ +static void test_free_NULL(void) +{ + TEST_MALLOC_MONITOR_SAVE + + free(NULL); + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(0); + + void *volatile alloc1 = malloc(MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc1); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(1); + + free(alloc1); + + TEST_ASSERT_CURRENT(0); + TEST_ASSERT_WATERMARK(1); +} + +static Test *tests_malloc_monitor(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_malloc_free), + new_TestFixture(test_calloc), + new_TestFixture(test_realloc), + new_TestFixture(test_free_NULL), + }; + + EMB_UNIT_TESTCALLER(tests, NULL, NULL, fixtures); + return (Test *)&tests; +} + +int main(void) +{ + puts("malloc_monitor test"); + TESTS_START(); + TESTS_RUN(tests_malloc_monitor()); + TESTS_END(); + + return 0; +} diff --git a/tests/sys/malloc_monitor/tests/01-run.py b/tests/sys/malloc_monitor/tests/01-run.py new file mode 100755 index 0000000000..9fb3d7f1aa --- /dev/null +++ b/tests/sys/malloc_monitor/tests/01-run.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2024 TU Dresden +# +# 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 +# directory for more details. + +import sys +from testrunner import run_check_unittests + + +if __name__ == "__main__": + sys.exit(run_check_unittests())