From 916a4fafbc8995cf03571789bdda534a750dcb6b 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 | 64 +++++++ 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 | 71 ++++++++ sys/malloc_monitor/malloc_monitor.c | 127 ++++++++++++++ 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 | 214 +++++++++++++++++++++++ tests/sys/malloc_monitor/tests/01-run.py | 14 ++ 16 files changed, 619 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 d5dea86d25982..e6e0a4c8f1914 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -302,7 +302,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 97ed70068a164..ec7297e1b3a58 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 0000000000000..249018421054d --- /dev/null +++ b/sys/include/malloc_monitor.h @@ -0,0 +1,64 @@ +/* + * 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 Heap Memory Usage Monitor + * @ingroup sys_memory_management + * + * @{ + * + * @brief monitor heap memory usage (calls to malloc/calloc/realloc/free) + * @author Mikolai Gütschow + */ + +#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 0000000000000..fb813e0e0317f --- /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_H +#define MALLOC_MONITOR_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_H */ + +/** + * @} + */ diff --git a/sys/malloc_monitor/Kconfig b/sys/malloc_monitor/Kconfig new file mode 100644 index 0000000000000..73aad813d4c18 --- /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 0000000000000..48422e909a47d --- /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 0000000000000..29729aa0a4bca --- /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 0000000000000..fa5180f8ec1d8 --- /dev/null +++ b/sys/malloc_monitor/doc.txt @@ -0,0 +1,71 @@ +/** +@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. + +# 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 0000000000000..8b761cca8fe8f --- /dev/null +++ b/sys/malloc_monitor/malloc_monitor.c @@ -0,0 +1,127 @@ +/* + * 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 "cpu.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, +}; + +void malloc_monitor_add(void *ptr, size_t size, uinttxtptr_t pc, char *func_prefix) +{ + if (ptr == NULL) return; + for (uint8_t i=0; i malloc_monitor.high_watermark) + malloc_monitor.high_watermark = malloc_monitor.current; +#if CONFIG_MODULE_SYS_MALLOC_MONITOR_VERBOSE + printf("%salloc(%" PRIuSIZE ") @ 0x%" PRIxTXTPTR " returned %p\n", + func_prefix, size, pc, ptr); +#endif + return; + } + } + (void)func_prefix; + (void)pc; + printf("malloc_monitor: maximum number of pointers to be monitored " + "(as set by CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE) exceeded.\n"); +} + +void malloc_monitor_rm(void *ptr, uinttxtptr_t pc) +{ + if (ptr == NULL) return; + 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; + } +#if CONFIG_MODULE_SYS_MALLOC_MONITOR_VERBOSE + printf("malloc_monitor: realloc(%p, %" PRIuSIZE ") @0x%" PRIxTXTPTR " returned %p\n", + ptr_old, size_new, pc, ptr_new); +#endif + return; + } + } + printf("malloc_monitor: realloc(%p) @ 0x%" PRIxTXTPTR " invalid\n", ptr_old, pc); +} + +size_t malloc_monitor_get_usage_current(void) { + return malloc_monitor.current; +} +size_t malloc_monitor_get_usage_high_watermark(void) { + return malloc_monitor.high_watermark; +} +void malloc_monitor_reset_high_watermark(void) { + malloc_monitor.high_watermark = malloc_monitor.current; +} + +/** @} */ diff --git a/sys/malloc_thread_safe/Kconfig b/sys/malloc_thread_safe/Kconfig index 2486e205ae485..d7ef706a399da 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 b5c98642b6e65..92929fd66940a 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 487440056f097..d7e7e8799c85c 100644 --- a/sys/malloc_thread_safe/malloc_wrappers.c +++ b/sys/malloc_thread_safe/malloc_wrappers.c @@ -25,6 +25,7 @@ #include "irq.h" #include "kernel_defines.h" #include "mutex.h" +#include "malloc_monitor_internal.h" extern void *__real_malloc(size_t size); extern void __real_free(void *ptr); @@ -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 0000000000000..ef34a8c649ae6 --- /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 0000000000000..363d4f3a79adc --- /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 0000000000000..8ddadce3a617d --- /dev/null +++ b/tests/sys/malloc_monitor/main.c @@ -0,0 +1,214 @@ +/* + * 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 *alloc1 = malloc(MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc1); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(1); + + void *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 *alloc1 = calloc(1, MALLOC_SIZE); + TEST_ASSERT_NOT_NULL(alloc1); + + TEST_ASSERT_CURRENT(1); + TEST_ASSERT_WATERMARK(1); + + void *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 *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 *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 0000000000000..9fb3d7f1aa1e0 --- /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())