feat(heap): Allow tracking of minimum free size for a given time frame

Implement a function to start tracking the minimum free size from the
moment the function is called.
Implement a function to stop tracking the minimum free size and restore
the minimum free size value calculated since startup.
Implement the tests related to this new feature.
This commit is contained in:
Guillaume Souchere 2023-12-01 06:55:32 +01:00
parent b2cc934b94
commit bb9d5a8d51
6 changed files with 176 additions and 4 deletions

View File

@ -574,6 +574,65 @@ size_t heap_caps_get_largest_free_block( uint32_t caps )
return info.largest_free_block;
}
static struct {
size_t *values; // Array of minimum_free_bytes used to keep the different values when starting monitoring
size_t counter; // Keep count of registered heap when monitoring to prevent any added heap to create an out of bound access on values
multi_heap_lock_t mux; // protect access to min_free_bytes_monitoring fields in start/stop monitoring functions
} min_free_bytes_monitoring = {NULL, 0, MULTI_HEAP_LOCK_STATIC_INITIALIZER};
esp_err_t heap_caps_monitor_local_minimum_free_size_start(void)
{
// update minimum_free_bytes on all affected heap, and store the "old value"
// as a snapshot of the heaps minimum_free_bytes state.
heap_t *heap = NULL;
MULTI_HEAP_LOCK(&min_free_bytes_monitoring.mux);
if (min_free_bytes_monitoring.values == NULL) {
SLIST_FOREACH(heap, &registered_heaps, next) {
min_free_bytes_monitoring.counter++;
}
min_free_bytes_monitoring.values = heap_caps_malloc(sizeof(size_t) * min_free_bytes_monitoring.counter, MALLOC_CAP_DEFAULT);
assert(min_free_bytes_monitoring.values != NULL && "not enough memory to store min_free_bytes value");
memset(min_free_bytes_monitoring.values, 0xFF, sizeof(size_t) * min_free_bytes_monitoring.counter);
}
heap = SLIST_FIRST(&registered_heaps);
for (size_t counter = 0; counter < min_free_bytes_monitoring.counter; counter++) {
size_t old_minimum = multi_heap_reset_minimum_free_bytes(heap->heap);
if (min_free_bytes_monitoring.values[counter] > old_minimum) {
min_free_bytes_monitoring.values[counter] = old_minimum;
}
heap = SLIST_NEXT(heap, next);
}
MULTI_HEAP_UNLOCK(&min_free_bytes_monitoring.mux);
return ESP_OK;
}
esp_err_t heap_caps_monitor_local_minimum_free_size_stop(void)
{
if (min_free_bytes_monitoring.values == NULL) {
return ESP_FAIL;
}
MULTI_HEAP_LOCK(&min_free_bytes_monitoring.mux);
heap_t *heap = SLIST_FIRST(&registered_heaps);
for (size_t counter = 0; counter < min_free_bytes_monitoring.counter; counter++) {
multi_heap_restore_minimum_free_bytes(heap->heap, min_free_bytes_monitoring.values[counter]);
heap = SLIST_NEXT(heap, next);
}
heap_caps_free(min_free_bytes_monitoring.values);
min_free_bytes_monitoring.values = NULL;
min_free_bytes_monitoring.counter = 0;
MULTI_HEAP_UNLOCK(&min_free_bytes_monitoring.mux);
return ESP_OK;
}
void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps )
{
memset(info, 0, sizeof(multi_heap_info_t));

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -241,6 +241,27 @@ size_t heap_caps_get_minimum_free_size( uint32_t caps );
*/
size_t heap_caps_get_largest_free_block( uint32_t caps );
/**
* @brief Start monitoring the value of minimum_free_bytes from the moment this
* function is called instead of from startup.
*
* @note This allows to detect local lows of the minimum_free_bytes value
* that wouldn't be detected otherwise.
*
* @return esp_err_t ESP_OK if the function executed properly
* ESP_FAIL if called when monitoring already active
*/
esp_err_t heap_caps_monitor_local_minimum_free_size_start(void);
/**
* @brief Stop monitoring the value of minimum_free_bytes. After this call
* the minimum_free_bytes value calculated from startup will be returned in
* heap_caps_get_info and heap_caps_get_minimum_free_size.
*
* @return esp_err_t ESP_OK if the function executed properly
* ESP_FAIL if called when monitoring not active
*/
esp_err_t heap_caps_monitor_local_minimum_free_size_stop(void);
/**
* @brief Get heap info for all regions with the given capabilities.

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -190,6 +190,23 @@ void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info);
*/
void *multi_heap_aligned_alloc_offs(multi_heap_handle_t heap, size_t size, size_t alignment, size_t offset);
/**
* @brief Reset the minimum_free_bytes value (setting it to free_bytes) and return the former value
*
* @param heap The heap in which the reset is taking place
* @return size_t the value of minimum_free_bytes before it is reset
*/
size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap);
/**
* @brief Set the value of minimum_free_bytes to new_minimum_free_bytes_value or keep
* the current value of minimum_free_bytes if it is smaller than new_minimum_free_bytes_value
*
* @param heap The heap in which the restore is taking place
* @param new_minimum_free_bytes_value The value to restore the minimum_free_bytes to
*/
void multi_heap_restore_minimum_free_bytes(multi_heap_handle_t heap, const size_t new_minimum_free_bytes_value);
#ifdef __cplusplus
}
#endif

View File

@ -11,6 +11,7 @@
#include <stddef.h>
#include <stdio.h>
#include <sys/cdefs.h>
#include <sys/param.h>
#include "multi_heap.h"
#include "multi_heap_internal.h"
@ -429,4 +430,22 @@ void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
info->largest_free_block = tlsf_fit_size(heap->heap_data, info->largest_free_block);
multi_heap_internal_unlock(heap);
}
#endif
#endif // CONFIG_HEAP_TLSF_USE_ROM_IMPL
size_t multi_heap_reset_minimum_free_bytes(multi_heap_handle_t heap)
{
multi_heap_internal_lock(heap);
const size_t old_minimum = heap->minimum_free_bytes;
heap->minimum_free_bytes = heap->free_bytes;
multi_heap_internal_unlock(heap);
return old_minimum;
}
void multi_heap_restore_minimum_free_bytes(multi_heap_handle_t heap, const size_t new_minimum_free_bytes_value)
{
multi_heap_internal_lock(heap);
// keep the value of minimum_free_bytes if it is lower than the value passed as parameter
heap->minimum_free_bytes = MIN(heap->minimum_free_bytes, new_minimum_free_bytes_value);
multi_heap_internal_unlock(heap);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

View File

@ -171,6 +171,62 @@ TEST_CASE("heap_caps metadata test", "[heap]")
TEST_ASSERT(after.minimum_free_bytes < original.total_free_bytes);
}
TEST_CASE("heap caps minimum free bytes monitoring", "[heap]")
{
printf("heap caps minimum free bytes monitoring local minimum\n");
uint32_t caps = MALLOC_CAP_DEFAULT;
size_t minimum_free_size_reference = heap_caps_get_minimum_free_size(caps);
// start monitoring the value of minimum free bytes
esp_err_t ret_val = heap_caps_monitor_local_minimum_free_size_start();
TEST_ASSERT_EQUAL(ret_val, ESP_OK);
// get the heap info and check that the value of minimum free bytes return
// is different from the previous one (before monitoring)
size_t local_minimum_free_size = heap_caps_get_minimum_free_size(caps);
TEST_ASSERT(local_minimum_free_size >= minimum_free_size_reference);
// allocate and free 400 bytes of memory.
size_t alloc_size = 400;
void *ptr = heap_caps_malloc(400, caps);
TEST_ASSERT(ptr != NULL);
heap_caps_free(ptr);
// Check the new value of minimum free bytes to make sure
// it is now lower than the previous one.
TEST_ASSERT(heap_caps_get_minimum_free_size(caps) <= local_minimum_free_size - alloc_size);
// stop monitoring
ret_val = heap_caps_monitor_local_minimum_free_size_stop();
TEST_ASSERT_EQUAL(ret_val, ESP_OK);
// get the heap info and check that the value of minimum free bytes is lower than
// the local minimum (since the local minimum didn't create a new all time minimum)
size_t free_size = heap_caps_get_minimum_free_size(caps);
TEST_ASSERT(local_minimum_free_size >= free_size);
}
TEST_CASE("heap caps minimum free bytes fault cases", "[heap]")
{
printf("heap caps minimum free bytes fault cases\n");
// start monitoring the value of minimum free bytes
esp_err_t ret_val = heap_caps_monitor_local_minimum_free_size_start();
TEST_ASSERT_EQUAL(ret_val, ESP_OK);
// calling start again should be allowed
ret_val = heap_caps_monitor_local_minimum_free_size_start();
TEST_ASSERT_EQUAL(ret_val, ESP_OK);
// stop the monitoring
ret_val = heap_caps_monitor_local_minimum_free_size_stop();
TEST_ASSERT_EQUAL(ret_val, ESP_OK);
// calling stop monitoring when monitoring is not active should fail
ret_val = heap_caps_monitor_local_minimum_free_size_stop();
TEST_ASSERT_NOT_EQUAL(ret_val, ESP_OK);
}
/* Small function runs from IRAM to check that malloc/free/realloc
all work OK when cache is disabled...
*/