-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sys: add malloc_monitor, deprecate malloc_tracing
- Loading branch information
1 parent
4d2b2f7
commit 916a4fa
Showing
16 changed files
with
619 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
* | ||
Check failure on line 11 in sys/include/malloc_monitor.h GitHub Actions / static-tests
|
||
* @{ | ||
* | ||
* @brief monitor heap memory usage (calls to malloc/calloc/realloc/free) | ||
* @author Mikolai Gütschow <mikolai.guetschow@tu-dresden.de> | ||
*/ | ||
|
||
#ifndef MALLOC_MONITOR_H | ||
#define MALLOC_MONITOR_H | ||
|
||
#include <assert.h> | ||
#include <stdint.h> | ||
#include <stddef.h> | ||
#include <string.h> | ||
|
||
#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. | ||
* | ||
Check failure on line 49 in sys/include/malloc_monitor.h GitHub Actions / static-tests
|
||
* 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 */ | ||
|
||
/** | ||
* @} | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <mikolai.guetschow@tu-dresden.de> | ||
*/ | ||
|
||
#ifndef MALLOC_MONITOR_H | ||
#define MALLOC_MONITOR_H | ||
|
||
#include <assert.h> | ||
#include <stdint.h> | ||
Check failure on line 21 in sys/include/malloc_monitor_internal.h GitHub Actions / static-tests
|
||
#include <stddef.h> | ||
#include <string.h> | ||
|
||
#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 */ | ||
|
||
/** | ||
* @} | ||
Check failure on line 72 in sys/include/malloc_monitor_internal.h GitHub Actions / static-tests
|
||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include $(RIOTBASE)/Makefile.base |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
USEMODULE += malloc_thread_safe |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <stddef.h> | ||
#include <stdio.h> | ||
|
||
#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. | ||
|
||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <mikolai.guetschow@tu-dresden.de> | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <string.h> | ||
|
||
#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<CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE; i++) { | ||
if (malloc_monitor.addr[i] == NULL) { | ||
malloc_monitor.addr[i] = ptr; | ||
malloc_monitor.size[i] = size; | ||
malloc_monitor.current += size; | ||
if (malloc_monitor.current > 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<CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE; i++) { | ||
if (malloc_monitor.addr[i] == ptr) { | ||
malloc_monitor.addr[i] = NULL; | ||
malloc_monitor.current -= malloc_monitor.size[i]; | ||
#if CONFIG_MODULE_SYS_MALLOC_MONITOR_VERBOSE | ||
printf("malloc_monitor: free(%p) @ 0x%" PRIxTXTPTR " \n", ptr, pc); | ||
#endif | ||
return; | ||
} | ||
} | ||
printf("malloc_monitor: free(%p) @ 0x%" PRIxTXTPTR " invalid\n", ptr, pc); | ||
} | ||
|
||
void malloc_monitor_mv(void *ptr_old, void *ptr_new, size_t size_new, uinttxtptr_t pc) | ||
{ | ||
if (ptr_old == NULL) { | ||
malloc_monitor_add(ptr_new, size_new, pc, "re"); | ||
return; | ||
} | ||
if (size_new == 0) { | ||
malloc_monitor_rm(ptr_old, pc); | ||
return; | ||
} | ||
if (ptr_new == NULL) return; | ||
for (uint8_t i=0; i<CONFIG_MODULE_SYS_MALLOC_MONITOR_SIZE; i++) { | ||
if (malloc_monitor.addr[i] == ptr_old) { | ||
malloc_monitor.addr[i] = ptr_new; | ||
size_t size_old = malloc_monitor.size[i]; | ||
malloc_monitor.size[i] = size_new; | ||
if (size_new > 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; | ||
} | ||
|
||
/** @} */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.