feat(examples): Update heap task tracking examples

update example to showcasee the new API of heap task tracking

- Add basic heap task traacking example
- Add advanced example for task tracking
This commit is contained in:
Guillaume Souchere
2025-02-14 08:49:38 +01:00
parent daf8f9edb6
commit d429b1fdbb
16 changed files with 651 additions and 140 deletions

View File

@@ -4,10 +4,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <inttypes.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <multi_heap.h>
#include <string.h>
#include "multi_heap_internal.h"
#include "heap_private.h"
#include "esp_heap_task_info.h"
@@ -619,11 +620,15 @@ esp_err_t heap_caps_get_single_task_stat(heap_single_task_stat_t *task_stat, Tas
return ESP_OK;
}
static void heap_caps_print_task_info(task_info_t *task_info, bool is_last_task_info)
static void heap_caps_print_task_info(FILE *stream, task_info_t *task_info, bool is_last_task_info)
{
if (stream == NULL) {
stream = stdout;
}
const char *task_info_visual = is_last_task_info ? " " : "";
const char *task_info_visual_start = is_last_task_info ? "" : "";
esp_rom_printf("%s %s: %s, CURRENT MEMORY USAGE %d, PEAK MEMORY USAGE %d, TOTAL HEAP USED %d:\n", task_info_visual_start,
fprintf(stream, "%s %s: %s, CURRENT MEMORY USAGE %d, PEAK MEMORY USAGE %d, TOTAL HEAP USED %d:\n", task_info_visual_start,
task_info->task_stat.is_alive ? "ALIVE" : "DELETED",
task_info->task_stat.name,
task_info->task_stat.overall_current_usage,
@@ -634,7 +639,7 @@ static void heap_caps_print_task_info(task_info_t *task_info, bool is_last_task_
STAILQ_FOREACH(heap_info, &task_info->heaps_stats, next_heap_stat) {
char *next_heap_visual = !STAILQ_NEXT(heap_info, next_heap_stat) ? " " : "";
char *next_heap_visual_start = !STAILQ_NEXT(heap_info, next_heap_stat) ? "" : "";
esp_rom_printf("%s %s HEAP: %s, CAPS: 0x%08lx, SIZE: %d, USAGE: CURRENT %d (%d%%), PEAK %d (%d%%), ALLOC COUNT: %d\n",
fprintf(stream, "%s %s HEAP: %s, CAPS: 0x%08lx, SIZE: %d, USAGE: CURRENT %d (%d%%), PEAK %d (%d%%), ALLOC COUNT: %d\n",
task_info_visual,
next_heap_visual_start,
heap_info->heap_stat.name,
@@ -648,7 +653,7 @@ static void heap_caps_print_task_info(task_info_t *task_info, bool is_last_task_
alloc_stats_t *alloc_stats = NULL;
STAILQ_FOREACH(alloc_stats, &heap_info->allocs_stats, next_alloc_stat) {
esp_rom_printf("%s %s ├ ALLOC %p, SIZE %d\n", task_info_visual,
fprintf(stream, "%s %s ├ ALLOC %p, SIZE %" PRIu32 "\n", task_info_visual,
next_heap_visual,
alloc_stats->alloc_stat.address,
alloc_stats->alloc_stat.size);
@@ -656,16 +661,20 @@ static void heap_caps_print_task_info(task_info_t *task_info, bool is_last_task_
}
}
static void heap_caps_print_task_overview(task_info_t *task_info, bool is_first_task_info, bool is_last_task_info)
static void heap_caps_print_task_overview(FILE *stream, task_info_t *task_info, bool is_first_task_info, bool is_last_task_info)
{
if (stream == NULL) {
stream = stdout;
}
if (is_first_task_info) {
esp_rom_printf("┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐\n");
esp_rom_printf("│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │\n");
esp_rom_printf("├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤\n");
fprintf(stream, "┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐\n");
fprintf(stream, "│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │\n");
fprintf(stream, "├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤\n");
}
task_stat_t task_stat = task_info->task_stat;
esp_rom_printf("│ %18s │ %7s │ %20d │ %17d │ %15d │\n",
fprintf(stream, "│ %18s │ %7s │ %20d │ %17d │ %15d │\n",
task_stat.name,
task_stat.is_alive ? "ALIVE " : "DELETED",
task_stat.overall_current_usage,
@@ -673,11 +682,11 @@ static void heap_caps_print_task_overview(task_info_t *task_info, bool is_first_
task_stat.heap_count);
if (is_last_task_info) {
esp_rom_printf("└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘\n");
fprintf(stream, "└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘\n");
}
}
void heap_caps_print_single_task_stat(TaskHandle_t task_handle)
void heap_caps_print_single_task_stat(FILE *stream, TaskHandle_t task_handle)
{
if (task_handle == NULL) {
task_handle = xTaskGetCurrentTaskHandle();
@@ -697,7 +706,7 @@ void heap_caps_print_single_task_stat(TaskHandle_t task_handle)
xSemaphoreGive(s_task_tracking_mutex);
}
void heap_caps_print_all_task_stat(void)
void heap_caps_print_all_task_stat(FILE *stream)
{
task_info_t *task_info = NULL;
@@ -709,7 +718,7 @@ void heap_caps_print_all_task_stat(void)
xSemaphoreGive(s_task_tracking_mutex);
}
void heap_caps_print_single_task_stat_overview(TaskHandle_t task_handle)
void heap_caps_print_single_task_stat_overview(FILE *stream, TaskHandle_t task_handle)
{
if (task_handle == NULL) {
task_handle = xTaskGetCurrentTaskHandle();
@@ -729,7 +738,7 @@ void heap_caps_print_single_task_stat_overview(TaskHandle_t task_handle)
xSemaphoreGive(s_task_tracking_mutex);
}
void heap_caps_print_all_task_stat_overview(void)
void heap_caps_print_all_task_stat_overview(FILE *stream)
{
task_info_t *task_info = NULL;
bool is_first_task_info = true;

View File

@@ -10,6 +10,7 @@
#ifdef CONFIG_HEAP_TASK_TRACKING
#include <stdint.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
@@ -167,16 +168,20 @@ esp_err_t heap_caps_get_single_task_stat(heap_single_task_stat_t *task_stat, Tas
*
* @note This function is an alternative to heap_caps_get_all_task_stat if the goal is just to print information
* and not manipulate them.
*
* @param steam The stream to dump to, if NULL then stdout is used
*/
void heap_caps_print_all_task_stat(void);
void heap_caps_print_all_task_stat(FILE *stream);
/**
* @brief Print summary information of all tasks
*
* @note The information printed by this function is an array formatted log of task_stat_t content for each running
* task (and deleted ones if HEAP_TRACK_DELETED_TASKS is enabled)
*
* @param steam The stream to dump to, if NULL then stdout is used
*/
void heap_caps_print_all_task_stat_overview(void);
void heap_caps_print_all_task_stat_overview(FILE *stream);
/**
* @brief Print heap memory usage and associated allocation information on each heap for a given task.
@@ -184,9 +189,10 @@ void heap_caps_print_all_task_stat_overview(void);
* @note This function is an alternative to heap_caps_get_single_task_stat if the goal is just to print information
* and not manipulate them.
*
* @param steam The stream to dump to, if NULL then stdout is used
* @param task_handle The task handle of the task to get memory usage and associated allocation information from.
*/
void heap_caps_print_single_task_stat(TaskHandle_t task_handle);
void heap_caps_print_single_task_stat(FILE *stream, TaskHandle_t task_handle);
/**
* @brief Print summary information of a given task
@@ -195,9 +201,10 @@ void heap_caps_print_single_task_stat(TaskHandle_t task_handle);
* task. This function will not print the task summary information if the given task is deleted and
* HEAP_TRACK_DELETED_TASKS is disabled.
*
* @param steam The stream to dump to, if NULL then stdout is used
* @param task_handle The task handle of the task to get memory usage and associated allocation information from.
*/
void heap_caps_print_single_task_stat_overview(TaskHandle_t task_handle);
void heap_caps_print_single_task_stat_overview(FILE *stream, TaskHandle_t task_handle);
/**
* @brief Allocate the memory used to store the heap and alloc statistics and fill task_stat

View File

@@ -202,7 +202,7 @@ Heap Task Tracking
Heap Task Tracking can be used to get per-task info for heap memory allocation. The application has to specify the heap capabilities for which the heap allocation is to be tracked.
Example code is provided in :example:`system/heap_task_tracking`.
Example applications are provided in :example:`system/heap_task_tracking/basic` and :example:`system/heap_task_tracking/advanced`.
.. _heap-tracing:
@@ -629,7 +629,8 @@ One way to differentiate between "real" and "false positive" memory leaks is to
Application Examples
--------------------
- :example:`system/heap_task_tracking` demonstrates the use of the heap task tracking feature to track heap memory allocated on a per-task basis.
- :example:`system/heap_task_tracking/basic` demonstrates the use of the overview feature of the heap task tracking, dumping per-task summary statistics on heap memory usage.
- :example:`system/heap_task_tracking/advanced` demonstrates the use of the statistics getter functions of the heap task tracking, accessing per-task complete statistic on the heap memory usage.
API Reference - Heap Tracing
----------------------------

View File

@@ -1,39 +0,0 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- |
# Heap Task Tracking Example
## Overview
The example creates a task which allocates random amount of memory in each iteration and demonstrates use of internal API to get heap info on per task basis running in a system.
Heap task tracking feature has dependency on some of the internal heap debugging features (e.g. heap poisoning) which allows to store task control block in metadata of each heap block.
This adds small memory overhead on per heap block and hence this feature should be used for debugging purpose only.
### Configure the project
To change the `Heap Corruption Detection level`, open the project configuration menu (`idf.py menuconfig`).
Navigate to `Component config -> Heap memory debugging` menu. In `Heap corruption detection` menu select either "Light Impact" or "Comprehensive".
**Note:** Enabling “Comprehensive” detection has a substantial runtime performance impact.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build and flash the project..
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
Task: Pre-Scheduler allocs -> CAP_8BIT: 5360 CAP_32BIT: 0
Task: esp_timer -> CAP_8BIT: 1724 CAP_32BIT: 0
Task: ipc0 -> CAP_8BIT: 8316 CAP_32BIT: 0
Task: main -> CAP_8BIT: 3480 CAP_32BIT: 0
Task: ipc1 -> CAP_8BIT: 12 CAP_32BIT: 0
Task: example_task -> CAP_8BIT: 696 CAP_32BIT: 0
```

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(advanced)

View File

@@ -0,0 +1,141 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- |
# Heap Task Tracking Basic Example
## Overview
The example creates a task which allocates random amount of memory and frees it and another task that allocates random amount of memory but never frees it.
The main then goes into a loop calling functions retrieving statistics for the "no leak" task, the "leaking" tasks and all tasks and printing them.
For each tasks, the following information is retrieved and printed:
- the task name
- the task status (running or deleted)
- the overall peak memory usage of the task
- the overall current memory usage of the task
For each heap used by a given task, the following information is printed:
- the heap name
- the heap caps
- the heap size
- the heap current memory usage by the task
- the heap peak memory usage by the task
- the number of blocks currently allocated in the heap by the task
For each block of memory allocated in a given heap by a given task, the following information is printed:
- the allocation address
- the allocation size
Because the heap task tracking feature requires additional metadata to be allocated for each memory allocations, the overall heap usage of the application is
greater than when the feature is disabled. For this reason, it is highly recommended to use the task tracking for debugging purpose only.
### Configure the project
- Enable thee option `Enable heap task tracking` by opening the project configuration menu (`idf.py menuconfig`) and navigate to `Component config -> Heap memory debugging` menu.
- (optional) Enable the option `Keep information about the memory usage on deleted tasks` if you wish to keep track of the information of a task after it has been deleted.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build and flash the project..
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
--------------------------------------------------------------------------------
PRINTING SINGLE TASK INFO
--------------------------------------------------------------------------------
no_leak_task: ALIVE : Peak Usage 10128, Current Usage 0
RAM: Caps: 1071118. Size 22308, Current Usage 0, Peak Usage 10128, alloc count 0
--------------------------------------------------------------------------------
PRINTING SINGLE TASK INFO
--------------------------------------------------------------------------------
leaking_task: ALIVE : Peak Usage 7232, Current Usage 6656
RAM: Caps: 1071118. Size 22308, Current Usage 6656, Peak Usage 7232, alloc count 1
0x3fceb878: Size: 6656
--------------------------------------------------------------------------------
PRINTING SINGLE TASK INFO
--------------------------------------------------------------------------------
no_leak_task: ALIVE : Peak Usage 10128, Current Usage 0
RAM: Caps: 1071118. Size 22308, Current Usage 0, Peak Usage 10128, alloc count 0
RAM: Caps: 1071118. Size 14832, Current Usage 0, Peak Usage 8960, alloc count 0
--------------------------------------------------------------------------------
PRINTING SINGLE TASK INFO
--------------------------------------------------------------------------------
leaking_task: ALIVE : Peak Usage 15040, Current Usage 9664
RAM: Caps: 1071118. Size 22308, Current Usage 6656, Peak Usage 12032, alloc count 1
0x3fceb878: Size: 6656
RAM: Caps: 1071118. Size 14832, Current Usage 3008, Peak Usage 3008, alloc count 1
0x3fc9a0e4: Size: 3008
[...]
--------------------------------------------------------------------------------
PRINTING ALL TASKS INFO
--------------------------------------------------------------------------------
leaking_task: DELETED: Peak Usage 19248, Current Usage 13616
RAM: Caps: 1071118. Size 22308, Current Usage 10608, Peak Usage 13296, alloc count 3
0x3fceb878: Size: 6656
0x3fceb634: Size: 368
0x3fcedd00: Size: 3584
RAM: Caps: 1071118. Size 14832, Current Usage 3008, Peak Usage 12224, alloc count 1
0x3fc9a0e4: Size: 3008
no_leak_task: DELETED: Peak Usage 10128, Current Usage 0
RAM: Caps: 1071118. Size 22308, Current Usage 0, Peak Usage 10128, alloc count 0
RAM: Caps: 1071118. Size 14832, Current Usage 0, Peak Usage 9728, alloc count 0
main: ALIVE : Peak Usage 7456, Current Usage 352
RAM: Caps: 1071118. Size 14832, Current Usage 264, Peak Usage 264, alloc count 3
0x3fc99cf4: Size: 88
0x3fc99e1c: Size: 88
0x3fc99e78: Size: 88
RAM: Caps: 1071118. Size 22308, Current Usage 88, Peak Usage 7192, alloc count 5
0x3fce99f8: Size: 20
0x3fce9a10: Size: 12
0x3fce9a20: Size: 16
0x3fce9a34: Size: 20
0x3fce9a4c: Size: 20
ipc1: ALIVE : Peak Usage 44, Current Usage 32
RAM: Caps: 1071118. Size 14832, Current Usage 32, Peak Usage 44, alloc count 2
0x3fc99dcc: Size: 16
0x3fc99df4: Size: 16
ipc0: ALIVE : Peak Usage 10092, Current Usage 10080
RAM: Caps: 1071118. Size 14832, Current Usage 10080, Peak Usage 10092, alloc count 10
0x3fc973b0: Size: 1312
0x3fc97950: Size: 344
0x3fc97ae4: Size: 16
0x3fc97b0c: Size: 4224
0x3fc98b90: Size: 344
0x3fc98d00: Size: 1568
0x3fc99338: Size: 344
0x3fc994a8: Size: 1568
0x3fc99ae0: Size: 344
0x3fc99c64: Size: 16
Pre-scheduler: ALIVE : Peak Usage 3364, Current Usage 3364
RAM: Caps: 1071118. Size 14832, Current Usage 3364, Peak Usage 3364, alloc count 22
0x3fc96410: Size: 164
0x3fc96538: Size: 12
0x3fc9655c: Size: 12
0x3fc96580: Size: 16
0x3fc965a8: Size: 24
0x3fc965d8: Size: 36
0x3fc96614: Size: 40
0x3fc96654: Size: 36
0x3fc96690: Size: 40
0x3fc966d0: Size: 88
0x3fc96740: Size: 88
0x3fc967b0: Size: 88
0x3fc96820: Size: 432
0x3fc969e8: Size: 88
0x3fc96a58: Size: 88
0x3fc96ac8: Size: 88
0x3fc96b38: Size: 132
0x3fc96bd4: Size: 132
0x3fc96c70: Size: 88
0x3fc96ce0: Size: 16
0x3fc96d08: Size: 1312
0x3fc97240: Size: 344
I (4504) main_task: Returned from app_main()
```

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "heap_task_tracking_advanced_main.c"
INCLUDE_DIRS "")

View File

@@ -0,0 +1,180 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Heap Task Tracking Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_heap_task_info.h"
#include "esp_heap_caps.h"
#include "esp_random.h"
#include "esp_log.h"
static void print_single_task_info(TaskHandle_t task_hdl)
{
heap_single_task_stat_t task_stat;
/* call API to dynamically allocate the memory necessary to store the
* information collected while calling heap_caps_get_single_task_stat */
const esp_err_t ret_val = heap_caps_alloc_single_task_stat_arrays(&task_stat, task_hdl);
assert(ret_val == ESP_OK);
/* collect the information */
heap_caps_get_single_task_stat(&task_stat, task_hdl);
/* process the information retrieved */
printf("\n--------------------------------------------------------------------------------\n");
printf("PRINTING SINGLE TASK INFO\n");
printf("--------------------------------------------------------------------------------\n");
printf("%s: %s: Peak Usage %"PRIu16", Current Usage %"PRIu16"\n", task_stat.stat.name,
task_stat.stat.is_alive ? "ALIVE " : "DELETED",
task_stat.stat.overall_peak_usage,
task_stat.stat.overall_current_usage);
for (size_t heap_idx = 0; heap_idx < task_stat.heap_count; heap_idx++) {
heap_stat_t heap_stat = task_stat.heap_stat_start[heap_idx];
printf(" %s: Caps: %"PRIu32". Size %"PRIu16", Current Usage %"PRIu16", Peak Usage %"PRIu16", alloc count %"PRIu16"\n", heap_stat.name,
heap_stat.caps,
heap_stat.size,
heap_stat.current_usage,
heap_stat.peak_usage,
heap_stat.alloc_count);
for (size_t alloc_idx = 0; alloc_idx < heap_stat.alloc_count; alloc_idx++) {
heap_task_block_t alloc_stat = heap_stat.alloc_stat[alloc_idx];
printf(" %p: Size: %"PRIu32"\n", alloc_stat.address, alloc_stat.size);
}
}
/* delete the memory dynamically allocated while calling heap_caps_alloc_all_task_stat_arrays */
heap_caps_free_single_task_stat_arrays(&task_stat);
}
static void print_all_tasks_info(void)
{
heap_all_tasks_stat_t tasks_stat;
/* call API to dynamically allocate the memory necessary to store the
* information collected while calling heap_caps_get_all_task_stat */
const esp_err_t ret_val = heap_caps_alloc_all_task_stat_arrays(&tasks_stat);
assert(ret_val == ESP_OK);
/* collect the information */
heap_caps_get_all_task_stat(&tasks_stat);
/* process the information retrieved */
printf("\n--------------------------------------------------------------------------------\n");
printf("PRINTING ALL TASKS INFO\n");
printf("--------------------------------------------------------------------------------\n");
for (size_t task_idx = 0; task_idx < tasks_stat.task_count; task_idx++) {
task_stat_t task_stat = tasks_stat.stat_arr[task_idx];
printf("%s: %s: Peak Usage %"PRIu16", Current Usage %"PRIu16"\n", task_stat.name,
task_stat.is_alive ? "ALIVE " : "DELETED",
task_stat.overall_peak_usage,
task_stat.overall_current_usage);
for (size_t heap_idx = 0; heap_idx < task_stat.heap_count; heap_idx++) {
heap_stat_t heap_stat = task_stat.heap_stat[heap_idx];
printf(" %s: Caps: %"PRIu32". Size %"PRIu16", Current Usage %"PRIu16", Peak Usage %"PRIu16", alloc count %"PRIu16"\n", heap_stat.name,
heap_stat.caps,
heap_stat.size,
heap_stat.current_usage,
heap_stat.peak_usage,
heap_stat.alloc_count);
for (size_t alloc_idx = 0; alloc_idx < heap_stat.alloc_count; alloc_idx++) {
heap_task_block_t alloc_stat = heap_stat.alloc_stat[alloc_idx];
printf(" %p: Size: %"PRIu32"\n", alloc_stat.address, alloc_stat.size);
}
}
}
/* delete the memory dynamically allocated while calling heap_caps_alloc_all_task_stat_arrays */
heap_caps_free_all_task_stat_arrays(&tasks_stat);
}
static void no_leak_task(void *args)
{
size_t size_a = 0;
size_t size_b = 0;
char *task_name = pcTaskGetName(*((TaskHandle_t*)args));
while(1) {
/* Allocate random amount of memory for demonstration */
size_a = (esp_random() % 10000) + 1;
size_b = (esp_random() % (10000 - size_a)) + 1;
void *ptr_a = heap_caps_malloc(size_a, MALLOC_CAP_DEFAULT);
void *ptr_b = heap_caps_malloc(size_b, MALLOC_CAP_DEFAULT);
if (ptr_a == NULL || ptr_b == NULL) {
ESP_LOGE(task_name, "Could not allocate heap memory");
abort();
}
heap_caps_free(ptr_a);
heap_caps_free(ptr_b);
// print the task statistics (passing NULL will print info for
// the currently running task)
print_single_task_info(NULL);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
static void leaking_task(void *args)
{
size_t size_a = 0;
size_t size_b = 0;
char *task_name = pcTaskGetName(*((TaskHandle_t*)args));
while(1) {
/* Allocate random amount of memory for demonstration */
size_a = (esp_random() % 10000) + 1;
size_b = (esp_random() % (10000 - size_a)) + 1;
void *ptr_a = heap_caps_malloc(size_a, MALLOC_CAP_DEFAULT);
void *ptr_b = heap_caps_malloc(size_b, MALLOC_CAP_DEFAULT);
if (ptr_a == NULL || ptr_b == NULL) {
ESP_LOGE(task_name, "Could not allocate heap memory");
abort();
}
heap_caps_free(ptr_a);
// don't free ptr_b on purpose to create unfreed memory for the task info to print
// heap_caps_free(ptr_b);
// print the task statistics (passing NULL will print info for
// the currently running task)
print_single_task_info(NULL);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
TaskHandle_t no_leak_task_hdl, leaking_task_hdl;
/* Create example task to demonstrate heap_task_tracking */
xTaskCreate(&no_leak_task, "no_leak_task", 3072, &no_leak_task_hdl, 0, &no_leak_task_hdl);
xTaskCreate(&leaking_task, "leaking_task", 3072, &leaking_task_hdl, 0, &leaking_task_hdl);
size_t counter = 4;
while(counter != 0) {
vTaskDelay(pdMS_TO_TICKS(1000));
counter--;
}
vTaskDelete(leaking_task_hdl);
vTaskDelete(no_leak_task_hdl);
print_all_tasks_info();
}

View File

@@ -0,0 +1,5 @@
# enable the task tracking feature
CONFIG_HEAP_TASK_TRACKING=y
# keep task tracking information after the task is deleted
CONFIG_HEAP_TRACK_DELETED_TASKS=y

View File

@@ -5,4 +5,4 @@ cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(heap_task_tracking)
project(basic)

View File

@@ -0,0 +1,156 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- |
# Heap Task Tracking Basic Example
## Overview
The example creates a task which allocates random amount of memory and frees it and another task that allocates random amount of memory but never frees it.
The main then goes into a loop printing the overview information of each task that allocated memory dynamically.
The information include:
- The task name
- The task status
- The current memory usage
- The peak memory usage
- The number of heaps currently used by the task
Because the heap task tracking feature requires additional metadata to be allocated for each memory allocations, the overall heap usage of the application is
greater than when the feature is disabled. For this reason, it is highly recommended to use the task tracking for debugging purpose only.
### Configure the project
- Enable thee option `Enable heap task tracking` by opening the project configuration menu (`idf.py menuconfig`) and navigate to `Component config -> Heap memory debugging` menu.
- (optional) Enable the option `Keep information about the memory usage on deleted tasks` if you wish to keep track of the information of a task after it has been deleted.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build and flash the project..
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
Starting task: no_leak_task
Starting task: leaking_task
PRINTING OVERVIEW STATISTICS OF EACH TASK
┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
│ leaking_task │ ALIVE │ 6656 │ 8064 │ 1 │
│ no_leak_task │ ALIVE │ 0 │ 7152 │ 1 │
│ main │ ALIVE │ 7412 │ 7412 │ 2 │
│ ipc1 │ ALIVE │ 32 │ 44 │ 1 │
│ ipc0 │ ALIVE │ 10080 │ 10092 │ 1 │
│ Pre-scheduler │ ALIVE │ 2236 │ 2236 │ 1 │
└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘
PRINTING OVERVIEW STATISTICS OF NO LEAK TASK
┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
│ no_leak_task │ ALIVE │ 0 │ 7152 │ 1 │
└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘
PRINTING OVERVIEW STATISTICS OF LEAKING TASK
┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
│ leaking_task │ ALIVE │ 6656 │ 8064 │ 1 │
└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘
[...]
Deleting task: leaking_task
PRINTING OVERVIEW STATISTICS OF EACH TASK
┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
│ leaking_task │ DELETED │ 11392 │ 11616 │ 1 │
│ no_leak_task │ ALIVE │ 0 │ 9408 │ 2 │
│ main │ ALIVE │ 3860 │ 7412 │ 2 │
│ ipc1 │ ALIVE │ 32 │ 44 │ 1 │
│ ipc0 │ ALIVE │ 10080 │ 10092 │ 1 │
│ Pre-scheduler │ ALIVE │ 2236 │ 2236 │ 1 │
└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘
PRINTING OVERVIEW STATISTICS OF NO LEAK TASK
┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
│ no_leak_task │ ALIVE │ 0 │ 9408 │ 2 │
└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘
PRINTING OVERVIEW STATISTICS OF LEAKING TASK
┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
│ leaking_task │ DELETED │ 11392 │ 11616 │ 1 │
└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘
Deleting task: no_leak_task
PRINTING OVERVIEW STATISTICS OF EACH TASK
┌────────────────────┬─────────┬──────────────────────┬───────────────────┬─────────────────┐
│ TASK │ STATUS │ CURRENT MEMORY USAGE │ PEAK MEMORY USAGE │ TOTAL HEAP USED │
├────────────────────┼─────────┼──────────────────────┼───────────────────┼─────────────────┤
│ leaking_task │ DELETED │ 11392 │ 11616 │ 1 │
│ no_leak_task │ DELETED │ 0 │ 9408 │ 2 │
│ main │ ALIVE │ 308 │ 7412 │ 2 │
│ ipc1 │ ALIVE │ 32 │ 44 │ 1 │
│ ipc0 │ ALIVE │ 10080 │ 10092 │ 1 │
│ Pre-scheduler │ ALIVE │ 2236 │ 2236 │ 1 │
└────────────────────┴─────────┴──────────────────────┴───────────────────┴─────────────────┘
PRINTING DETAILED STATISTICS OF EACH TASK
├ DELETED: leaking_task, CURRENT MEMORY USAGE 11392, PEAK MEMORY USAGE 11616, TOTAL HEAP USED 1:
│ └ HEAP: RAM, CAPS: 0x0010580e, SIZE: 22308, USAGE: CURRENT 11392 (51%), PEAK 11616 (52%), ALLOC COUNT: 3
│ ├ ALLOC 0x3fcebbb8, SIZE 6656
│ ├ ALLOC 0x3fced5bc, SIZE 3584
│ ├ ALLOC 0x3fceb718, SIZE 1152
├ DELETED: no_leak_task, CURRENT MEMORY USAGE 0, PEAK MEMORY USAGE 9408, TOTAL HEAP USED 2:
│ ├ HEAP: RAM, CAPS: 0x0010580e, SIZE: 22308, USAGE: CURRENT 0 (0%), PEAK 7152 (32%), ALLOC COUNT: 0
│ └ HEAP: RAM, CAPS: 0x0010580e, SIZE: 344400, USAGE: CURRENT 0 (0%), PEAK 9216 (2%), ALLOC COUNT: 0
├ ALIVE: main, CURRENT MEMORY USAGE 308, PEAK MEMORY USAGE 7412, TOTAL HEAP USED 2:
│ ├ HEAP: RAM, CAPS: 0x0010580e, SIZE: 344400, USAGE: CURRENT 220 (0%), PEAK 220 (0%), ALLOC COUNT: 2
│ │ ├ ALLOC 0x3fc99024, SIZE 88
│ │ ├ ALLOC 0x3fc99124, SIZE 132
│ └ HEAP: RAM, CAPS: 0x0010580e, SIZE: 22308, USAGE: CURRENT 88 (0%), PEAK 7192 (32%), ALLOC COUNT: 5
│ ├ ALLOC 0x3fce99f8, SIZE 20
│ ├ ALLOC 0x3fce9a10, SIZE 12
│ ├ ALLOC 0x3fce9a20, SIZE 16
│ ├ ALLOC 0x3fce9a34, SIZE 20
│ ├ ALLOC 0x3fce9a4c, SIZE 20
├ ALIVE: ipc1, CURRENT MEMORY USAGE 32, PEAK MEMORY USAGE 44, TOTAL HEAP USED 1:
│ └ HEAP: RAM, CAPS: 0x0010580e, SIZE: 344400, USAGE: CURRENT 32 (0%), PEAK 44 (0%), ALLOC COUNT: 2
│ ├ ALLOC 0x3fc990fc, SIZE 16
│ ├ ALLOC 0x3fc991c0, SIZE 16
├ ALIVE: ipc0, CURRENT MEMORY USAGE 10080, PEAK MEMORY USAGE 10092, TOTAL HEAP USED 1:
│ └ HEAP: RAM, CAPS: 0x0010580e, SIZE: 344400, USAGE: CURRENT 10080 (2%), PEAK 10092 (2%), ALLOC COUNT: 10
│ ├ ALLOC 0x3fc966e0, SIZE 1312
│ ├ ALLOC 0x3fc96c80, SIZE 344
│ ├ ALLOC 0x3fc96e14, SIZE 16
│ ├ ALLOC 0x3fc96e3c, SIZE 4224
│ ├ ALLOC 0x3fc97ec0, SIZE 344
│ ├ ALLOC 0x3fc98030, SIZE 1568
│ ├ ALLOC 0x3fc98668, SIZE 344
│ ├ ALLOC 0x3fc987d8, SIZE 1568
│ ├ ALLOC 0x3fc98e10, SIZE 344
│ ├ ALLOC 0x3fc98f94, SIZE 16
└ ALIVE: Pre-scheduler, CURRENT MEMORY USAGE 2236, PEAK MEMORY USAGE 2236, TOTAL HEAP USED 1:
└ HEAP: RAM, CAPS: 0x0010580e, SIZE: 344400, USAGE: CURRENT 2236 (0%), PEAK 2236 (0%), ALLOC COUNT: 11
├ ALLOC 0x3fc95cb0, SIZE 164
├ ALLOC 0x3fc95dd8, SIZE 12
├ ALLOC 0x3fc95dfc, SIZE 12
├ ALLOC 0x3fc95e20, SIZE 16
├ ALLOC 0x3fc95e48, SIZE 24
├ ALLOC 0x3fc95e78, SIZE 88
├ ALLOC 0x3fc95ee8, SIZE 88
├ ALLOC 0x3fc95f58, SIZE 88
├ ALLOC 0x3fc95fc8, SIZE 88
├ ALLOC 0x3fc96038, SIZE 1312
├ ALLOC 0x3fc96570, SIZE 344
I (5949) main_task: Returned from app_main()
```

View File

@@ -0,0 +1,117 @@
/*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Heap Task Tracking Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_heap_task_info.h"
#include "esp_heap_caps.h"
#include "esp_random.h"
#include "esp_log.h"
static void no_leak_task(void *args)
{
size_t size_a = 0;
size_t size_b = 0;
char *task_name = pcTaskGetName(*((TaskHandle_t*)args));
printf("Starting task: %s\n", task_name);
while(1) {
/* Allocate random amount of memory for demonstration */
size_a = (esp_random() % 10000) + 1;
size_b = (esp_random() % (10000 - size_a)) + 1;
void *ptr_a = heap_caps_malloc(size_a, MALLOC_CAP_DEFAULT);
void *ptr_b = heap_caps_malloc(size_b, MALLOC_CAP_DEFAULT);
if (ptr_a == NULL || ptr_b == NULL) {
ESP_LOGE(task_name, "Could not allocate heap memory");
abort();
}
heap_caps_free(ptr_a);
heap_caps_free(ptr_b);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
static void leaking_task(void *args)
{
size_t size_a = 0;
size_t size_b = 0;
char *task_name = pcTaskGetName(*((TaskHandle_t*)args));
printf("Starting task: %s\n", task_name);
while(1) {
/* Allocate random amount of memory for demonstration */
size_a = (esp_random() % 10000) + 1;
size_b = (esp_random() % (10000 - size_a)) + 1;
void *ptr_a = heap_caps_malloc(size_a, MALLOC_CAP_DEFAULT);
void *ptr_b = heap_caps_malloc(size_b, MALLOC_CAP_DEFAULT);
if (ptr_a == NULL || ptr_b == NULL) {
ESP_LOGE(task_name, "Could not allocate heap memory");
abort();
}
heap_caps_free(ptr_a);
// heap_caps_free(ptr_b);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
TaskHandle_t no_leak_task_hdl, leaking_task_hdl;
/* Create example task to demonstrate heap_task_tracking */
xTaskCreate(&no_leak_task, "no_leak_task", 3072, &no_leak_task_hdl, 5, &no_leak_task_hdl);
xTaskCreate(&leaking_task, "leaking_task", 3072, &leaking_task_hdl, 5, &leaking_task_hdl);
/* print task statistic periodically */
for(size_t counter = 0; counter < 4; counter++) {
/* print the overview stats of every task */
printf("\n PRINTING OVERVIEW STATISTICS OF EACH TASK\n");
heap_caps_print_all_task_stat_overview(stdout);
/* print the overview statistics of the no leak task */
printf("\n PRINTING OVERVIEW STATISTICS OF NO LEAK TASK\n");
heap_caps_print_single_task_stat_overview(stdout, no_leak_task_hdl);
/* print the overview statistics of the leaking task */
printf("\n PRINTING OVERVIEW STATISTICS OF LEAKING TASK\n");
heap_caps_print_single_task_stat_overview(stdout, leaking_task_hdl);
if (counter == 2) {
/* delete the leaking task and let the no leak task run
* for some more time */
printf("Deleting task: %s\n", pcTaskGetName(leaking_task_hdl));
vTaskDelete(leaking_task_hdl);
}
/* wait for a second before running the loop again*/
vTaskDelay(pdMS_TO_TICKS(1000));
}
/* Delete the no leak task */
printf("Deleting task: %s\n", pcTaskGetName(no_leak_task_hdl));
vTaskDelete(no_leak_task_hdl);
/* print overview information of every task */
printf("\n PRINTING OVERVIEW STATISTICS OF EACH TASK\n");
heap_caps_print_all_task_stat_overview(stdout);
/* print detailed statistics for every task */
printf("\n PRINTING DETAILED STATISTICS OF EACH TASK\n");
heap_caps_print_all_task_stat(stdout);
}

View File

@@ -0,0 +1,5 @@
# enable the task tracking feature
CONFIG_HEAP_TASK_TRACKING=y
# keep task tracking information after the task is deleted
CONFIG_HEAP_TRACK_DELETED_TASKS=y

View File

@@ -1,78 +0,0 @@
/* Heap Task Tracking Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_heap_task_info.h"
#include "esp_log.h"
#include "esp_random.h"
#define MAX_TASK_NUM 20 // Max number of per tasks info that it can store
#define MAX_BLOCK_NUM 20 // Max number of per block info that it can store
static size_t s_prepopulated_num = 0;
static heap_task_totals_t s_totals_arr[MAX_TASK_NUM];
static heap_task_block_t s_block_arr[MAX_BLOCK_NUM];
static void esp_dump_per_task_heap_info(void)
{
heap_task_info_params_t heap_info = {0};
heap_info.caps[0] = MALLOC_CAP_8BIT; // Gets heap with CAP_8BIT capabilities
heap_info.mask[0] = MALLOC_CAP_8BIT;
heap_info.caps[1] = MALLOC_CAP_32BIT; // Gets heap info with CAP_32BIT capabilities
heap_info.mask[1] = MALLOC_CAP_32BIT;
heap_info.tasks = NULL; // Passing NULL captures heap info for all tasks
heap_info.num_tasks = 0;
heap_info.totals = s_totals_arr; // Gets task wise allocation details
heap_info.num_totals = &s_prepopulated_num;
heap_info.max_totals = MAX_TASK_NUM; // Maximum length of "s_totals_arr"
heap_info.blocks = s_block_arr; // Gets block wise allocation details. For each block, gets owner task, address and size
heap_info.max_blocks = MAX_BLOCK_NUM; // Maximum length of "s_block_arr"
heap_caps_get_per_task_info(&heap_info);
for (int i = 0 ; i < *heap_info.num_totals; i++) {
printf("Task: %s -> CAP_8BIT: %d CAP_32BIT: %d\n",
heap_info.totals[i].task ? pcTaskGetName(heap_info.totals[i].task) : "Pre-Scheduler allocs" ,
heap_info.totals[i].size[0], // Heap size with CAP_8BIT capabilities
heap_info.totals[i].size[1]); // Heap size with CAP32_BIT capabilities
}
printf("\n\n");
}
static void example_task(void *args)
{
uint32_t size = 0;
const char *TAG = "example_task";
while (1) {
/*
* Allocate random amount of memory for demonstration
*/
size = (esp_random() % 1000);
void *ptr = malloc(size);
if (ptr == NULL) {
ESP_LOGE(TAG, "Could not allocate heap memory");
abort();
}
esp_dump_per_task_heap_info();
free(ptr);
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
void app_main(void)
{
/*
* Create example task to demonstrate heap_task_tracking
*/
xTaskCreate(&example_task, "example_task", 3072, NULL, 5, NULL);
}

View File

@@ -1 +0,0 @@
CONFIG_HEAP_TASK_TRACKING=y