Skip to content

Commit

Permalink
feat(heap): Allow tracking of minimum free size for a given time frame
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
SoucheSouche committed Dec 21, 2023
1 parent b2cc934 commit bb9d5a8
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 4 deletions.
59 changes: 59 additions & 0 deletions components/heap/heap_caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
23 changes: 22 additions & 1 deletion components/heap/include/esp_heap_caps.h
Original file line number Diff line number Diff line change
@@ -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
*/
Expand Down Expand Up @@ -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.
Expand Down
19 changes: 18 additions & 1 deletion components/heap/include/multi_heap.h
Original file line number Diff line number Diff line change
@@ -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
*/
Expand Down Expand Up @@ -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
21 changes: 20 additions & 1 deletion components/heap/multi_heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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);
}
2 changes: 1 addition & 1 deletion components/heap/multi_heap_internal.h
Original file line number Diff line number Diff line change
@@ -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
*/
Expand Down
56 changes: 56 additions & 0 deletions components/heap/test_apps/heap_tests/main/test_malloc_caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -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...
*/
Expand Down

0 comments on commit bb9d5a8

Please sign in to comment.