Skip to content

Commit

Permalink
Merge branch 'feat/heap-reset-heap-info' into 'master'
Browse files Browse the repository at this point in the history
feat(heap): Allow tracking of minimum free size for a given time frame

Closes IDF-8782

See merge request espressif/esp-idf!27577
  • Loading branch information
SoucheSouche committed Dec 21, 2023
2 parents b655a7a + bb9d5a8 commit 7f64b42
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 8 deletions.
67 changes: 63 additions & 4 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 All @@ -584,13 +643,13 @@ void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps )
multi_heap_info_t hinfo;
multi_heap_get_info(heap->heap, &hinfo);

info->total_free_bytes += hinfo.total_free_bytes - MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(0);
info->total_free_bytes += hinfo.total_free_bytes - MULTI_HEAP_BLOCK_OWNER_SIZE();
info->total_allocated_bytes += (hinfo.total_allocated_bytes -
hinfo.allocated_blocks * MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(0));
hinfo.allocated_blocks * MULTI_HEAP_BLOCK_OWNER_SIZE());
info->largest_free_block = MAX(info->largest_free_block,
hinfo.largest_free_block);
info->largest_free_block -= info->largest_free_block ? MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(0) : 0;
info->minimum_free_bytes += hinfo.minimum_free_bytes - MULTI_HEAP_ADD_BLOCK_OWNER_SIZE(0);
info->largest_free_block -= info->largest_free_block ? MULTI_HEAP_BLOCK_OWNER_SIZE() : 0;
info->minimum_free_bytes += hinfo.minimum_free_bytes - MULTI_HEAP_BLOCK_OWNER_SIZE();
info->allocated_blocks += hinfo.allocated_blocks;
info->free_blocks += hinfo.free_blocks;
info->total_blocks += hinfo.total_blocks;
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 7f64b42

Please sign in to comment.