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.
pull/12863/head
Guillaume Souchere 2023-12-01 06:55:32 +01:00
rodzic b2cc934b94
commit bb9d5a8d51
6 zmienionych plików z 176 dodań i 4 usunięć

Wyświetl plik

@ -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));

Wyświetl plik

@ -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.

Wyświetl plik

@ -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

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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
*/

Wyświetl plik

@ -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...
*/