From 7c63bf7574c4616ccff023c97dc8507e210ba59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 16 May 2024 15:43:27 +0100 Subject: [PATCH] Revert "TLSF memory allocator. Less free flash, moar free ram. (#3572)" (#3651) * Revert "TLSF memory allocator. Less free flash, moar free ram. (#3572)" This reverts commit 1d17206e2358583a7feac2e142218f5b4ca38148. * Fix PVS warnings * github: logging for ticket number checks to stdout * memgr: removed offending todo --------- Co-authored-by: hedger --- .../workflows/lint_and_submodule_check.yml | 2 + .gitmodules | 3 - .pvsoptions | 2 +- .../debug/unit_tests/furi/furi_memmgr_test.c | 262 +----- .../debug/unit_tests/furi/furi_test.c | 2 - .../nfc/plugins/supported_cards/skylanders.c | 16 +- applications/services/cli/cli_commands.c | 48 +- furi/core/memmgr.c | 39 +- furi/core/memmgr.h | 25 +- furi/core/memmgr_heap.c | 786 ++++++++++++------ furi/core/memmgr_heap.h | 12 +- furi/core/thread.c | 4 +- furi/flipper.c | 13 +- lib/SConscript | 1 - lib/flipper_application/elf/elf_file.c | 8 +- lib/tlsf | 1 - lib/tlsf.scons | 21 - targets/f18/api_symbols.csv | 12 +- targets/f18/target.json | 3 +- targets/f7/api_symbols.csv | 12 +- targets/f7/fatfs/sector_cache.c | 2 +- targets/f7/src/update.c | 14 +- targets/f7/target.json | 3 +- 23 files changed, 651 insertions(+), 640 deletions(-) delete mode 160000 lib/tlsf delete mode 100644 lib/tlsf.scons diff --git a/.github/workflows/lint_and_submodule_check.yml b/.github/workflows/lint_and_submodule_check.yml index 3f4d8c79b..3063d943d 100644 --- a/.github/workflows/lint_and_submodule_check.yml +++ b/.github/workflows/lint_and_submodule_check.yml @@ -54,6 +54,8 @@ jobs: echo "\`\`\`" >> $GITHUB_STEP_SUMMARY; echo "$MISSING_TICKETS" >> $GITHUB_STEP_SUMMARY; echo "\`\`\`" >> $GITHUB_STEP_SUMMARY; + echo "Error: Missing issue number in comment(s):"; + echo "$MISSING_TICKETS"; exit 1; else echo "No new TODOs without tickets found" >> $GITHUB_STEP_SUMMARY; diff --git a/.gitmodules b/.gitmodules index a8d12803c..c4c68a6a7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -41,6 +41,3 @@ [submodule "documentation/doxygen/doxygen-awesome-css"] path = documentation/doxygen/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css.git -[submodule "lib/tlsf"] - path = lib/tlsf - url = https://github.com/espressif/tlsf diff --git a/.pvsoptions b/.pvsoptions index 590a34de8..8606eef15 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -1 +1 @@ ---ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/tlsf -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/microtar -e lib/mlib -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e lib/mjs -e */arm-none-eabi/* +--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/microtar -e lib/mlib -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e lib/mjs -e */arm-none-eabi/* diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c index 399e2d418..01e2c17f6 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -1,5 +1,8 @@ #include "../minunit.h" -#include +#include +#include +#include +#include void test_furi_memmgr(void) { void* ptr; @@ -34,260 +37,3 @@ void test_furi_memmgr(void) { } free(ptr); } - -static void test_memmgr_malloc(const size_t allocation_size) { - uint8_t* ptr = NULL; - const char* error_message = NULL; - - FURI_CRITICAL_ENTER(); - - ptr = malloc(allocation_size); - - // test that we can allocate memory - if(ptr == NULL) { - error_message = "malloc failed"; - } - - // test that memory is zero-initialized after allocation - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] != 0) { - error_message = "memory is not zero-initialized after malloc"; - break; - } - } - memset(ptr, 0x55, allocation_size); - free(ptr); - - // test that memory is zero-initialized after free - // we know that allocator can use this memory for inner purposes - // so we check that memory at least partially zero-initialized - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuse-after-free" - - size_t zero_count = 0; - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] == 0) { - zero_count++; - } - } - -#pragma GCC diagnostic pop - - // check that at least 75% of memory is zero-initialized - if(zero_count < (allocation_size * 0.75)) { - error_message = "seems that memory is not zero-initialized after free (malloc)"; - } - - FURI_CRITICAL_EXIT(); - - if(error_message != NULL) { - mu_fail(error_message); - } -} - -static void test_memmgr_realloc(const size_t allocation_size) { - uint8_t* ptr = NULL; - const char* error_message = NULL; - - FURI_CRITICAL_ENTER(); - - ptr = realloc(ptr, allocation_size); - - // test that we can allocate memory - if(ptr == NULL) { - error_message = "realloc(NULL) failed"; - } - - // test that memory is zero-initialized after allocation - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] != 0) { - error_message = "memory is not zero-initialized after realloc(NULL)"; - break; - } - } - - memset(ptr, 0x55, allocation_size); - - ptr = realloc(ptr, allocation_size * 2); - - // test that we can reallocate memory - if(ptr == NULL) { - error_message = "realloc failed"; - } - - // test that memory content is preserved - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] != 0x55) { - error_message = "memory is not reallocated after realloc"; - break; - } - } - - // test that remaining memory is zero-initialized - size_t non_zero_count = 0; - for(size_t i = allocation_size; i < allocation_size * 2; i++) { - if(ptr[i] != 0) { - non_zero_count += 1; - } - } - - // check that at most of memory is zero-initialized - // we know that allocator not always can restore content size from a pointer - // so we check against small threshold - if(non_zero_count > 4) { - error_message = "seems that memory is not zero-initialized after realloc"; - } - - uint8_t* null_ptr = realloc(ptr, 0); - - // test that we can free memory - if(null_ptr != NULL) { - error_message = "realloc(0) failed"; - } - - // test that memory is zero-initialized after realloc(0) - // we know that allocator can use this memory for inner purposes - // so we check that memory at least partially zero-initialized - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuse-after-free" - - size_t zero_count = 0; - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] == 0) { - zero_count++; - } - } - -#pragma GCC diagnostic pop - - // check that at least 75% of memory is zero-initialized - if(zero_count < (allocation_size * 0.75)) { - error_message = "seems that memory is not zero-initialized after realloc(0)"; - } - - FURI_CRITICAL_EXIT(); - - if(error_message != NULL) { - mu_fail(error_message); - } -} - -static void test_memmgr_alloc_aligned(const size_t allocation_size, const size_t alignment) { - uint8_t* ptr = NULL; - const char* error_message = NULL; - - FURI_CRITICAL_ENTER(); - - ptr = aligned_alloc(alignment, allocation_size); - - // test that we can allocate memory - if(ptr == NULL) { - error_message = "aligned_alloc failed"; - } - - // test that memory is aligned - if(((uintptr_t)ptr % alignment) != 0) { - error_message = "memory is not aligned after aligned_alloc"; - } - - // test that memory is zero-initialized after allocation - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] != 0) { - error_message = "memory is not zero-initialized after aligned_alloc"; - break; - } - } - memset(ptr, 0x55, allocation_size); - free(ptr); - - // test that memory is zero-initialized after free - // we know that allocator can use this memory for inner purposes - // so we check that memory at least partially zero-initialized - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuse-after-free" - - size_t zero_count = 0; - for(size_t i = 0; i < allocation_size; i++) { - if(ptr[i] == 0) { - zero_count++; - } - } - -#pragma GCC diagnostic pop - - // check that at least 75% of memory is zero-initialized - if(zero_count < (allocation_size * 0.75)) { - error_message = "seems that memory is not zero-initialized after free (aligned_alloc)"; - } - - FURI_CRITICAL_EXIT(); - - if(error_message != NULL) { - mu_fail(error_message); - } -} - -void test_furi_memmgr_advanced(void) { - const size_t sizes[] = {50, 100, 500, 1000, 5000, 10000}; - const size_t sizes_count = sizeof(sizes) / sizeof(sizes[0]); - const size_t alignments[] = {4, 8, 16, 32, 64, 128, 256, 512, 1024}; - const size_t alignments_count = sizeof(alignments) / sizeof(alignments[0]); - - // do test without memory fragmentation - { - for(size_t i = 0; i < sizes_count; i++) { - test_memmgr_malloc(sizes[i]); - } - - for(size_t i = 0; i < sizes_count; i++) { - test_memmgr_realloc(sizes[i]); - } - - for(size_t i = 0; i < sizes_count; i++) { - for(size_t j = 0; j < alignments_count; j++) { - test_memmgr_alloc_aligned(sizes[i], alignments[j]); - } - } - } - - // do test with memory fragmentation - { - void* blocks[sizes_count]; - void* guards[sizes_count - 1]; - - // setup guards - for(size_t i = 0; i < sizes_count; i++) { - blocks[i] = malloc(sizes[i]); - if(i < sizes_count - 1) { - guards[i] = malloc(sizes[i]); - } - } - - for(size_t i = 0; i < sizes_count; i++) { - free(blocks[i]); - } - - // do test - for(size_t i = 0; i < sizes_count; i++) { - test_memmgr_malloc(sizes[i]); - } - - for(size_t i = 0; i < sizes_count; i++) { - test_memmgr_realloc(sizes[i]); - } - - for(size_t i = 0; i < sizes_count; i++) { - for(size_t j = 0; j < alignments_count; j++) { - test_memmgr_alloc_aligned(sizes[i], alignments[j]); - } - } - - // cleanup guards - for(size_t i = 0; i < sizes_count - 1; i++) { - free(guards[i]); - } - } -} \ No newline at end of file diff --git a/applications/debug/unit_tests/furi/furi_test.c b/applications/debug/unit_tests/furi/furi_test.c index e0b5916d5..e287f9927 100644 --- a/applications/debug/unit_tests/furi/furi_test.c +++ b/applications/debug/unit_tests/furi/furi_test.c @@ -9,7 +9,6 @@ void test_furi_concurrent_access(void); void test_furi_pubsub(void); void test_furi_memmgr(void); -void test_furi_memmgr_advanced(void); static int foo = 0; @@ -38,7 +37,6 @@ MU_TEST(mu_test_furi_memmgr) { // this test is not accurate, but gives a basic understanding // that memory management is working fine test_furi_memmgr(); - test_furi_memmgr_advanced(); } MU_TEST_SUITE(test_suite) { diff --git a/applications/main/nfc/plugins/supported_cards/skylanders.c b/applications/main/nfc/plugins/supported_cards/skylanders.c index a60a5dba3..fca1c3185 100644 --- a/applications/main/nfc/plugins/supported_cards/skylanders.c +++ b/applications/main/nfc/plugins/supported_cards/skylanders.c @@ -92,6 +92,7 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { furi_string_cat_printf(name, "Lightning Rod"); break; case 0x0004: + case 0x0194: furi_string_cat_printf(name, "Bash"); break; case 0x0005: @@ -128,6 +129,7 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { furi_string_cat_printf(name, "Slam Bam"); break; case 0x0010: + case 0x01A0: furi_string_cat_printf(name, "Spyro"); break; case 0x0011: @@ -137,6 +139,7 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { furi_string_cat_printf(name, "Double Trouble"); break; case 0x0013: + case 0x01A3: furi_string_cat_printf(name, "Trigger Happy"); break; case 0x0014: @@ -170,6 +173,7 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { furi_string_cat_printf(name, "Hex"); break; case 0x001E: + case 0x01AE: furi_string_cat_printf(name, "Chop Chop"); break; case 0x001F: @@ -331,18 +335,6 @@ static uint8_t fill_name(const uint16_t id, FuriString* name) { case 0x0134: furi_string_cat_printf(name, "Midnight Museum"); break; - case 0x0194: - furi_string_cat_printf(name, "Bash"); - break; - case 0x01A0: - furi_string_cat_printf(name, "Spyro"); - break; - case 0x01A3: - furi_string_cat_printf(name, "Trigger Happy"); - break; - case 0x01AE: - furi_string_cat_printf(name, "Chop Chop"); - break; case 0x01C2: furi_string_cat_printf(name, "Gusto"); break; diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index 56d05785f..43f1c01c4 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -425,34 +425,8 @@ void cli_command_free(Cli* cli, FuriString* args, void* context) { printf("Minimum heap size: %zu\r\n", memmgr_get_minimum_free_heap()); printf("Maximum heap block: %zu\r\n", memmgr_heap_get_max_free_block()); - printf("Aux pool total free: %zu\r\n", memmgr_aux_pool_get_free()); - printf("Aux pool max free block: %zu\r\n", memmgr_pool_get_max_block()); -} - -typedef struct { - void* addr; - size_t size; -} FreeBlockInfo; - -#define FREE_BLOCK_INFO_MAX 128 - -typedef struct { - FreeBlockInfo free_blocks[FREE_BLOCK_INFO_MAX]; - size_t free_blocks_count; -} FreeBlockContext; - -static bool free_block_walker(void* pointer, size_t size, bool used, void* context) { - FreeBlockContext* free_blocks = (FreeBlockContext*)context; - if(!used) { - if(free_blocks->free_blocks_count < FREE_BLOCK_INFO_MAX) { - free_blocks->free_blocks[free_blocks->free_blocks_count].addr = pointer; - free_blocks->free_blocks[free_blocks->free_blocks_count].size = size; - free_blocks->free_blocks_count++; - } else { - return false; - } - } - return true; + printf("Pool free: %zu\r\n", memmgr_pool_get_free()); + printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block()); } void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { @@ -460,23 +434,7 @@ void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { UNUSED(args); UNUSED(context); - FreeBlockContext* free_blocks = malloc(sizeof(FreeBlockContext)); - free_blocks->free_blocks_count = 0; - - memmgr_heap_walk_blocks(free_block_walker, free_blocks); - - for(size_t i = 0; i < free_blocks->free_blocks_count; i++) { - printf( - "A %p S %zu\r\n", - (void*)free_blocks->free_blocks[i].addr, - free_blocks->free_blocks[i].size); - } - - if(free_blocks->free_blocks_count == FREE_BLOCK_INFO_MAX) { - printf("... and more\r\n"); - } - - free(free_blocks); + memmgr_heap_printf_free_blocks(); } void cli_command_i2c(Cli* cli, FuriString* args, void* context) { diff --git a/furi/core/memmgr.c b/furi/core/memmgr.c index 768d44890..768adc05d 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -4,8 +4,6 @@ #include extern void* pvPortMalloc(size_t xSize); -extern void* pvPortAllocAligned(size_t xSize, size_t xAlignment); -extern void* pvPortRealloc(void* pv, size_t xSize); extern void vPortFree(void* pv); extern size_t xPortGetFreeHeapSize(void); extern size_t xPortGetTotalHeapSize(void); @@ -20,7 +18,18 @@ void free(void* ptr) { } void* realloc(void* ptr, size_t size) { - return pvPortRealloc(ptr, size); + if(size == 0) { + vPortFree(ptr); + return NULL; + } + + void* p = pvPortMalloc(size); + if(ptr != NULL) { + memcpy(p, ptr, size); + vPortFree(ptr); + } + + return p; } void* calloc(size_t count, size_t size) { @@ -38,10 +47,6 @@ char* strdup(const char* s) { return y; } -void* aligned_alloc(size_t alignment, size_t size) { - return pvPortAllocAligned(size, alignment); -} - size_t memmgr_get_free_heap(void) { return xPortGetFreeHeapSize(); } @@ -74,17 +79,33 @@ void* __wrap__realloc_r(struct _reent* r, void* ptr, size_t size) { return realloc(ptr, size); } -void* memmgr_aux_pool_alloc(size_t size) { +void* memmgr_alloc_from_pool(size_t size) { void* p = furi_hal_memory_alloc(size); if(p == NULL) p = malloc(size); return p; } -size_t memmgr_aux_pool_get_free(void) { +size_t memmgr_pool_get_free(void) { return furi_hal_memory_get_free(); } size_t memmgr_pool_get_max_block(void) { return furi_hal_memory_max_pool_block(); +} + +void* aligned_malloc(size_t size, size_t alignment) { + void* p1; // original block + void** p2; // aligned block + int offset = alignment - 1 + sizeof(void*); + if((p1 = (void*)malloc(size + offset)) == NULL) { + return NULL; + } + p2 = (void**)(((size_t)(p1) + offset) & ~(alignment - 1)); + p2[-1] = p1; + return p2; +} + +void aligned_free(void* p) { + free(((void**)p)[-1]); } \ No newline at end of file diff --git a/furi/core/memmgr.h b/furi/core/memmgr.h index 796a1f537..bc0c35faa 100644 --- a/furi/core/memmgr.h +++ b/furi/core/memmgr.h @@ -36,22 +36,37 @@ size_t memmgr_get_total_heap(void); size_t memmgr_get_minimum_free_heap(void); /** - * @brief Allocate memory from the auxiliary memory pool. That memory can't be freed. + * An aligned version of malloc, used when you need to get the aligned space on the heap + * Freeing the received address is performed ONLY through the aligned_free function + * @param size + * @param alignment + * @return void* + */ +void* aligned_malloc(size_t size, size_t alignment); + +/** + * Freed space obtained through the aligned_malloc function + * @param p pointer to result of aligned_malloc + */ +void aligned_free(void* p); + +/** + * @brief Allocate memory from separate memory pool. That memory can't be freed. * * @param size * @return void* */ -void* memmgr_aux_pool_alloc(size_t size); +void* memmgr_alloc_from_pool(size_t size); /** - * @brief Get the auxiliary pool free memory size + * @brief Get free memory pool size * * @return size_t */ -size_t memmgr_aux_pool_get_free(void); +size_t memmgr_pool_get_free(void); /** - * @brief Get max free block size from the auxiliary memory pool + * @brief Get max free block size from memory pool * * @return size_t */ diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 3dfc7f5ac..3cee0d377 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -1,18 +1,124 @@ -#include -#include -#include +/* + * FreeRTOS Kernel V10.2.1 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * A sample implementation of pvPortMalloc() and vPortFree() that combines + * (coalescences) adjacent memory blocks as they are freed, and in so doing + * limits memory fragmentation. + * + * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the + * memory management pages of http://www.FreeRTOS.org for more information. + */ + +#include "memmgr_heap.h" +#include "check.h" +#include +#include +#include +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + #include #include -#include +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#ifdef HEAP_PRINT_DEBUG +#error This feature is broken, logging transport must be replaced with RTT +#endif + +#if(configSUPPORT_DYNAMIC_ALLOCATION == 0) +#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 +#endif + +/* Block sizes must not get too small. */ +#define heapMINIMUM_BLOCK_SIZE ((size_t)(xHeapStructSize << 1)) + +/* Assumes 8bit bytes! */ +#define heapBITS_PER_BYTE ((size_t)8) + +/* Heap start end symbols provided by linker */ extern const void __heap_start__; extern const void __heap_end__; +uint8_t* ucHeap = (uint8_t*)&__heap_start__; -static tlsf_t tlsf = NULL; -static size_t heap_used = 0; -static size_t heap_max_used = 0; +/* Define the linked list structure. This is used to link free blocks in order +of their memory address. */ +typedef struct A_BLOCK_LINK { + struct A_BLOCK_LINK* pxNextFreeBlock; /*<< The next free block in the list. */ + size_t xBlockSize; /*<< The size of the free block. */ +} BlockLink_t; -// Allocation tracking types +/*-----------------------------------------------------------*/ + +/* + * Inserts a block of memory that is being freed into the correct position in + * the list of free memory blocks. The block being freed will be merged with + * the block in front it and/or the block behind it if the memory blocks are + * adjacent to each other. + */ +static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert); + +/* + * Called automatically to setup the required heap structures the first time + * pvPortMalloc() is called. + */ +static void prvHeapInit(void); + +/*-----------------------------------------------------------*/ + +/* The size of the structure placed at the beginning of each allocated memory +block must by correctly byte aligned. */ +static const size_t xHeapStructSize = (sizeof(BlockLink_t) + ((size_t)(portBYTE_ALIGNMENT - 1))) & + ~((size_t)portBYTE_ALIGNMENT_MASK); + +/* Create a couple of list links to mark the start and end of the list. */ +static BlockLink_t xStart, *pxEnd = NULL; + +/* Keeps track of the number of free bytes remaining, but says nothing about +fragmentation. */ +static size_t xFreeBytesRemaining = 0U; +static size_t xMinimumEverFreeBytesRemaining = 0U; + +/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize +member of an BlockLink_t structure is set then the block belongs to the +application. When the bit is free the block is still part of the free heap +space. */ +static size_t xBlockAllocatedBit = 0; + +/* Furi heap extension */ +#include + +/* Allocation tracking types */ DICT_DEF2(MemmgrHeapAllocDict, uint32_t, uint32_t) //-V1048 DICT_DEF2( //-V1048 @@ -22,35 +128,17 @@ DICT_DEF2( //-V1048 MemmgrHeapAllocDict_t, DICT_OPLIST(MemmgrHeapAllocDict)) -// Thread allocation tracing storage +/* Thread allocation tracing storage */ static MemmgrHeapThreadDict_t memmgr_heap_thread_dict = {0}; static volatile uint32_t memmgr_heap_thread_trace_depth = 0; -static inline void memmgr_lock(void) { - vTaskSuspendAll(); -} - -static inline void memmgr_unlock(void) { - xTaskResumeAll(); -} - -static inline size_t memmgr_get_heap_size(void) { - return (size_t)&__heap_end__ - (size_t)&__heap_start__; -} - -// Initialize tracing storage -static void memmgr_heap_init(void) { +/* Initialize tracing storage on start */ +void memmgr_heap_init(void) { MemmgrHeapThreadDict_init(memmgr_heap_thread_dict); } -__attribute__((constructor)) static void memmgr_init(void) { - size_t pool_size = (size_t)&__heap_end__ - (size_t)&__heap_start__; - tlsf = tlsf_create_with_pool((void*)&__heap_start__, pool_size, pool_size); - memmgr_heap_init(); -} - void memmgr_heap_enable_thread_trace(FuriThreadId thread_id) { - memmgr_lock(); + vTaskSuspendAll(); { memmgr_heap_thread_trace_depth++; furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) == NULL); @@ -60,20 +148,53 @@ void memmgr_heap_enable_thread_trace(FuriThreadId thread_id) { MemmgrHeapAllocDict_clear(alloc_dict); memmgr_heap_thread_trace_depth--; } - memmgr_unlock(); + (void)xTaskResumeAll(); } void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) { - memmgr_lock(); + vTaskSuspendAll(); { memmgr_heap_thread_trace_depth++; furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id)); memmgr_heap_thread_trace_depth--; } - memmgr_unlock(); + (void)xTaskResumeAll(); } -static inline void memmgr_heap_trace_malloc(void* pointer, size_t size) { +size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) { + size_t leftovers = MEMMGR_HEAP_UNKNOWN; + vTaskSuspendAll(); + { + memmgr_heap_thread_trace_depth++; + MemmgrHeapAllocDict_t* alloc_dict = + MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); + if(alloc_dict) { + leftovers = 0; + MemmgrHeapAllocDict_it_t alloc_dict_it; + for(MemmgrHeapAllocDict_it(alloc_dict_it, *alloc_dict); + !MemmgrHeapAllocDict_end_p(alloc_dict_it); + MemmgrHeapAllocDict_next(alloc_dict_it)) { + MemmgrHeapAllocDict_itref_t* data = MemmgrHeapAllocDict_ref(alloc_dict_it); + if(data->key != 0) { + uint8_t* puc = (uint8_t*)data->key; + puc -= xHeapStructSize; + BlockLink_t* pxLink = (void*)puc; + + if((pxLink->xBlockSize & xBlockAllocatedBit) != 0 && + pxLink->pxNextFreeBlock == NULL) { + leftovers += data->value; + } + } + } + } + memmgr_heap_thread_trace_depth--; + } + (void)xTaskResumeAll(); + return leftovers; +} + +#undef traceMALLOC +static inline void traceMALLOC(void* pointer, size_t size) { FuriThreadId thread_id = furi_thread_get_current_id(); if(thread_id && memmgr_heap_thread_trace_depth == 0) { memmgr_heap_thread_trace_depth++; @@ -86,7 +207,9 @@ static inline void memmgr_heap_trace_malloc(void* pointer, size_t size) { } } -static inline void memmgr_heap_trace_free(void* pointer) { +#undef traceFREE +static inline void traceFREE(void* pointer, size_t size) { + UNUSED(size); FuriThreadId thread_id = furi_thread_get_current_id(); if(thread_id && memmgr_heap_thread_trace_depth == 0) { memmgr_heap_thread_trace_depth++; @@ -101,248 +224,441 @@ static inline void memmgr_heap_trace_free(void* pointer) { } } -size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id) { - size_t leftovers = MEMMGR_HEAP_UNKNOWN; - memmgr_lock(); - { - memmgr_heap_thread_trace_depth++; - MemmgrHeapAllocDict_t* alloc_dict = - MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); - if(alloc_dict) { - leftovers = 0; - MemmgrHeapAllocDict_it_t alloc_dict_it; - for(MemmgrHeapAllocDict_it(alloc_dict_it, *alloc_dict); - !MemmgrHeapAllocDict_end_p(alloc_dict_it); - MemmgrHeapAllocDict_next(alloc_dict_it)) { - MemmgrHeapAllocDict_itref_t* data = MemmgrHeapAllocDict_ref(alloc_dict_it); - if(data->key != 0) { - block_header_t* block = block_from_ptr((uint8_t*)data->key); - if(!block_is_free(block)) { - leftovers += data->value; - } - } - } - } - memmgr_heap_thread_trace_depth--; - } - memmgr_unlock(); - return leftovers; -} - -static bool tlsf_walker_max_free(void* ptr, size_t size, int used, void* user) { - UNUSED(ptr); - - bool free = !used; - size_t* max_free_block_size = (size_t*)user; - if(free && size > *max_free_block_size) { - *max_free_block_size = size; - } - - return true; -} - size_t memmgr_heap_get_max_free_block(void) { - size_t max_free_block_size = 0; + size_t max_free_size = 0; + BlockLink_t* pxBlock; + vTaskSuspendAll(); - memmgr_lock(); + pxBlock = xStart.pxNextFreeBlock; + while(pxBlock->pxNextFreeBlock != NULL) { + if(pxBlock->xBlockSize > max_free_size) { + max_free_size = pxBlock->xBlockSize; + } + pxBlock = pxBlock->pxNextFreeBlock; + } - pool_t pool = tlsf_get_pool(tlsf); - tlsf_walk_pool(pool, tlsf_walker_max_free, &max_free_block_size); - - memmgr_unlock(); - - return max_free_block_size; + xTaskResumeAll(); + return max_free_size; } -typedef struct { - BlockWalker walker; - void* context; -} BlockWalkerWrapper; +void memmgr_heap_printf_free_blocks(void) { + BlockLink_t* pxBlock; + //can be enabled once we can do printf with a locked scheduler + //vTaskSuspendAll(); -static bool tlsf_walker_wrapper(void* ptr, size_t size, int used, void* user) { - BlockWalkerWrapper* wrapper = (BlockWalkerWrapper*)user; - return wrapper->walker(ptr, size, used, wrapper->context); + pxBlock = xStart.pxNextFreeBlock; + while(pxBlock->pxNextFreeBlock != NULL) { + printf("A %p S %lu\r\n", (void*)pxBlock, (uint32_t)pxBlock->xBlockSize); + pxBlock = pxBlock->pxNextFreeBlock; + } + + //xTaskResumeAll(); } -void memmgr_heap_walk_blocks(BlockWalker walker, void* context) { - memmgr_lock(); +#ifdef HEAP_PRINT_DEBUG +char* ultoa(unsigned long num, char* str, int radix) { + char temp[33]; // at radix 2 the string is at most 32 + 1 null long. + int temp_loc = 0; + int digit; + int str_loc = 0; - BlockWalkerWrapper wrapper = {walker, context}; - pool_t pool = tlsf_get_pool(tlsf); - tlsf_walk_pool(pool, tlsf_walker_wrapper, &wrapper); + //construct a backward string of the number. + do { + digit = (unsigned long)num % ((unsigned long)radix); + if(digit < 10) + temp[temp_loc++] = digit + '0'; + else + temp[temp_loc++] = digit - 10 + 'A'; + num = ((unsigned long)num) / ((unsigned long)radix); + } while((unsigned long)num > 0); - memmgr_unlock(); + temp_loc--; + + //now reverse the string. + while(temp_loc >= 0) { // while there are still chars + str[str_loc++] = temp[temp_loc--]; + } + str[str_loc] = 0; // add null termination. + + return str; } -void* pvPortMalloc(size_t xSize) { - // memory management in ISR is not allowed +static void print_heap_init(void) { + char tmp_str[33]; + size_t heap_start = (size_t)&__heap_start__; + size_t heap_end = (size_t)&__heap_end__; + + // {PHStart|heap_start|heap_end} + FURI_CRITICAL_ENTER(); + furi_log_puts("{PHStart|"); + ultoa(heap_start, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts("|"); + ultoa(heap_end, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); + FURI_CRITICAL_EXIT(); +} + +static void print_heap_malloc(void* ptr, size_t size) { + char tmp_str[33]; + const char* name = furi_thread_get_name(furi_thread_get_current_id()); + if(!name) { + name = ""; + } + + // {thread name|m|address|size} + FURI_CRITICAL_ENTER(); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|m|0x"); + ultoa((unsigned long)ptr, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts("|"); + utoa(size, tmp_str, 10); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); + FURI_CRITICAL_EXIT(); +} + +static void print_heap_free(void* ptr) { + char tmp_str[33]; + const char* name = furi_thread_get_name(furi_thread_get_current_id()); + if(!name) { + name = ""; + } + + // {thread name|f|address} + FURI_CRITICAL_ENTER(); + furi_log_puts("{"); + furi_log_puts(name); + furi_log_puts("|f|0x"); + ultoa((unsigned long)ptr, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts("}\r\n"); + FURI_CRITICAL_EXIT(); +} +#endif +/*-----------------------------------------------------------*/ + +void* pvPortMalloc(size_t xWantedSize) { + BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; + void* pvReturn = NULL; + size_t to_wipe = xWantedSize; + if(FURI_IS_IRQ_MODE()) { furi_crash("memmgt in ISR"); } - memmgr_lock(); +#ifdef HEAP_PRINT_DEBUG + BlockLink_t* print_heap_block = NULL; +#endif - // allocate block - void* data = tlsf_malloc(tlsf, xSize); - if(data == NULL) { - if(xSize == 0) { - furi_crash("malloc(0)"); + /* If this is the first call to malloc then the heap will require + initialisation to setup the list of free blocks. */ + if(pxEnd == NULL) { +#ifdef HEAP_PRINT_DEBUG + print_heap_init(); +#endif + + vTaskSuspendAll(); + { + prvHeapInit(); + memmgr_heap_init(); + } + (void)xTaskResumeAll(); + } else { + mtCOVERAGE_TEST_MARKER(); + } + + vTaskSuspendAll(); + { + /* Check the requested block size is not so large that the top bit is + set. The top bit of the block size member of the BlockLink_t structure + is used to determine who owns the block - the application or the + kernel, so it must be free. */ + if((xWantedSize & xBlockAllocatedBit) == 0) { + /* The wanted size is increased so it can contain a BlockLink_t + structure in addition to the requested amount of bytes. */ + if(xWantedSize > 0) { + xWantedSize += xHeapStructSize; + + /* Ensure that blocks are always aligned to the required number + of bytes. */ + if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0x00) { + /* Byte alignment required. */ + xWantedSize += (portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK)); + configASSERT((xWantedSize & portBYTE_ALIGNMENT_MASK) == 0); + } else { + mtCOVERAGE_TEST_MARKER(); + } + } else { + mtCOVERAGE_TEST_MARKER(); + } + + if((xWantedSize > 0) && (xWantedSize <= xFreeBytesRemaining)) { + /* Traverse the list from the start (lowest address) block until + one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + while((pxBlock->xBlockSize < xWantedSize) && (pxBlock->pxNextFreeBlock != NULL)) { + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } + + /* If the end marker was reached then a block of adequate size + was not found. */ + if(pxBlock != pxEnd) { + /* Return the memory space pointed to - jumping over the + BlockLink_t structure at its start. */ + pvReturn = + (void*)(((uint8_t*)pxPreviousBlock->pxNextFreeBlock) + xHeapStructSize); + + /* This block is being returned for use so must be taken out + of the list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into + two. */ + if((pxBlock->xBlockSize - xWantedSize) > heapMINIMUM_BLOCK_SIZE) { + /* This block is to be split into two. Create a new + block following the number of bytes requested. The void + cast is used to prevent byte alignment warnings from the + compiler. */ + pxNewBlockLink = (void*)(((uint8_t*)pxBlock) + xWantedSize); + configASSERT((((size_t)pxNewBlockLink) & portBYTE_ALIGNMENT_MASK) == 0); + + /* Calculate the sizes of two blocks split from the + single block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList(pxNewBlockLink); + } else { + mtCOVERAGE_TEST_MARKER(); + } + + xFreeBytesRemaining -= pxBlock->xBlockSize; + + if(xFreeBytesRemaining < xMinimumEverFreeBytesRemaining) { + xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; + } else { + mtCOVERAGE_TEST_MARKER(); + } + + /* The block is being returned - it is allocated and owned + by the application and has no "next" block. */ + pxBlock->xBlockSize |= xBlockAllocatedBit; + pxBlock->pxNextFreeBlock = NULL; + +#ifdef HEAP_PRINT_DEBUG + print_heap_block = pxBlock; +#endif + } else { + mtCOVERAGE_TEST_MARKER(); + } + } else { + mtCOVERAGE_TEST_MARKER(); + } } else { - furi_crash("out of memory"); + mtCOVERAGE_TEST_MARKER(); + } + + traceMALLOC(pvReturn, xWantedSize); + } + (void)xTaskResumeAll(); + +#ifdef HEAP_PRINT_DEBUG + print_heap_malloc(print_heap_block, print_heap_block->xBlockSize & ~xBlockAllocatedBit); +#endif + +#if(configUSE_MALLOC_FAILED_HOOK == 1) + { + if(pvReturn == NULL) { + extern void vApplicationMallocFailedHook(void); + vApplicationMallocFailedHook(); + } else { + mtCOVERAGE_TEST_MARKER(); } } +#endif - // update heap usage - heap_used += tlsf_block_size(data); - heap_used += tlsf_alloc_overhead(); - if(heap_used > heap_max_used) { - heap_max_used = heap_used; - } + configASSERT((((size_t)pvReturn) & (size_t)portBYTE_ALIGNMENT_MASK) == 0); - // trace allocation - memmgr_heap_trace_malloc(data, xSize); - - memmgr_unlock(); - - // clear block content - memset(data, 0, xSize); - - return data; + furi_check(pvReturn, xWantedSize ? "out of memory" : "malloc(0)"); + pvReturn = memset(pvReturn, 0, to_wipe); + return pvReturn; } +/*-----------------------------------------------------------*/ void vPortFree(void* pv) { - // memory management in ISR is not allowed + uint8_t* puc = (uint8_t*)pv; + BlockLink_t* pxLink; + if(FURI_IS_IRQ_MODE()) { furi_crash("memmgt in ISR"); } - // ignore NULL pointer if(pv != NULL) { - memmgr_lock(); + /* The memory being freed will have an BlockLink_t structure immediately + before it. */ + puc -= xHeapStructSize; - // get block size - size_t block_size = tlsf_block_size(pv); + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = (void*)puc; - // clear block content - memset(pv, 0, block_size); + /* Check the block is actually allocated. */ + configASSERT((pxLink->xBlockSize & xBlockAllocatedBit) != 0); + configASSERT(pxLink->pxNextFreeBlock == NULL); - // update heap usage - heap_used -= block_size; - heap_used -= tlsf_alloc_overhead(); + if((pxLink->xBlockSize & xBlockAllocatedBit) != 0) { + if(pxLink->pxNextFreeBlock == NULL) { + /* The block is being returned to the heap - it is no longer + allocated. */ + pxLink->xBlockSize &= ~xBlockAllocatedBit; - // free - tlsf_free(tlsf, pv); +#ifdef HEAP_PRINT_DEBUG + print_heap_free(pxLink); +#endif - // trace free - memmgr_heap_trace_free(pv); + vTaskSuspendAll(); + { + furi_assert((size_t)pv >= SRAM_BASE); + furi_assert((size_t)pv < SRAM_BASE + 1024 * 256); + furi_assert(pxLink->xBlockSize >= xHeapStructSize); + furi_assert((pxLink->xBlockSize - xHeapStructSize) < 1024 * 256); - memmgr_unlock(); - } -} - -extern void* pvPortAllocAligned(size_t xSize, size_t xAlignment) { - // memory management in ISR is not allowed - if(FURI_IS_IRQ_MODE()) { - furi_crash("memmgt in ISR"); - } - - // alignment must be power of 2 - if((xAlignment & (xAlignment - 1)) != 0) { - furi_crash("invalid alignment"); - } - - memmgr_lock(); - - // allocate block - void* data = tlsf_memalign(tlsf, xAlignment, xSize); - if(data == NULL) { - if(xSize == 0) { - furi_crash("malloc_aligned(0)"); + /* Add this block to the list of free blocks. */ + xFreeBytesRemaining += pxLink->xBlockSize; + traceFREE(pv, pxLink->xBlockSize); + memset(pv, 0, pxLink->xBlockSize - xHeapStructSize); + prvInsertBlockIntoFreeList(((BlockLink_t*)pxLink)); + } + (void)xTaskResumeAll(); + } else { + mtCOVERAGE_TEST_MARKER(); + } } else { - furi_crash("out of memory"); + mtCOVERAGE_TEST_MARKER(); } + } else { +#ifdef HEAP_PRINT_DEBUG + print_heap_free(pv); +#endif } - - // update heap usage - heap_used += tlsf_block_size(data); - heap_used += tlsf_alloc_overhead(); - if(heap_used > heap_max_used) { - heap_max_used = heap_used; - } - - // trace allocation - memmgr_heap_trace_malloc(data, xSize); - - memmgr_unlock(); - - // clear block content - memset(data, 0, xSize); - - return data; -} - -extern void* pvPortRealloc(void* pv, size_t xSize) { - // realloc(ptr, 0) is equivalent to free(ptr) - if(xSize == 0) { - vPortFree(pv); - return NULL; - } - - // realloc(NULL, size) is equivalent to malloc(size) - if(pv == NULL) { - return pvPortMalloc(xSize); - } - - /* realloc things */ - - // memory management in ISR is not allowed - if(FURI_IS_IRQ_MODE()) { - furi_crash("memmgt in ISR"); - } - - memmgr_lock(); - - // trace old block as free - size_t old_size = tlsf_block_size(pv); - - // trace free - memmgr_heap_trace_free(pv); - - // reallocate block - void* data = tlsf_realloc(tlsf, pv, xSize); - if(data == NULL) { - furi_crash("out of memory"); - } - - // update heap usage - heap_used -= old_size; - heap_used += tlsf_block_size(data); - if(heap_used > heap_max_used) { - heap_max_used = heap_used; - } - - // trace allocation - memmgr_heap_trace_malloc(data, xSize); - - memmgr_unlock(); - - // clear remain block content, if the new size is bigger - // can't guarantee that all data will be zeroed, cos tlsf_block_size is not always the same as xSize - if(xSize > old_size) { - memset((uint8_t*)data + old_size, 0, xSize - old_size); - } - - return data; -} - -size_t xPortGetFreeHeapSize(void) { - return memmgr_get_heap_size() - heap_used - tlsf_size(tlsf); } +/*-----------------------------------------------------------*/ size_t xPortGetTotalHeapSize(void) { - return memmgr_get_heap_size(); + return (size_t)&__heap_end__ - (size_t)&__heap_start__; } +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize(void) { + return xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ size_t xPortGetMinimumEverFreeHeapSize(void) { - return memmgr_get_heap_size() - heap_max_used - tlsf_size(tlsf); -} \ No newline at end of file + return xMinimumEverFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks(void) { + /* This just exists to keep the linker quiet. */ +} +/*-----------------------------------------------------------*/ + +static void prvHeapInit(void) { + BlockLink_t* pxFirstFreeBlock; + uint8_t* pucAlignedHeap; + size_t uxAddress; + size_t xTotalHeapSize = (size_t)&__heap_end__ - (size_t)&__heap_start__; + + /* Ensure the heap starts on a correctly aligned boundary. */ + uxAddress = (size_t)ucHeap; + + if((uxAddress & portBYTE_ALIGNMENT_MASK) != 0) { + uxAddress += (portBYTE_ALIGNMENT - 1); + uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK); + xTotalHeapSize -= uxAddress - (size_t)ucHeap; + } + + pucAlignedHeap = (uint8_t*)uxAddress; + + /* xStart is used to hold a pointer to the first item in the list of free + blocks. The void cast is used to prevent compiler warnings. */ + xStart.pxNextFreeBlock = (void*)pucAlignedHeap; + xStart.xBlockSize = (size_t)0; + + /* pxEnd is used to mark the end of the list of free blocks and is inserted + at the end of the heap space. */ + uxAddress = ((size_t)pucAlignedHeap) + xTotalHeapSize; + uxAddress -= xHeapStructSize; + uxAddress &= ~((size_t)portBYTE_ALIGNMENT_MASK); + pxEnd = (void*)uxAddress; + pxEnd->xBlockSize = 0; + pxEnd->pxNextFreeBlock = NULL; + + /* To start with there is a single free block that is sized to take up the + entire heap space, minus the space taken by pxEnd. */ + pxFirstFreeBlock = (void*)pucAlignedHeap; + pxFirstFreeBlock->xBlockSize = uxAddress - (size_t)pxFirstFreeBlock; + pxFirstFreeBlock->pxNextFreeBlock = pxEnd; + + /* Only one block exists - and it covers the entire usable heap space. */ + xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + + /* Work out the position of the top bit in a size_t variable. */ + xBlockAllocatedBit = ((size_t)1) << ((sizeof(size_t) * heapBITS_PER_BYTE) - 1); +} +/*-----------------------------------------------------------*/ + +static void prvInsertBlockIntoFreeList(BlockLink_t* pxBlockToInsert) { + BlockLink_t* pxIterator; + uint8_t* puc; + + /* Iterate through the list until a block is found that has a higher address + than the block being inserted. */ + for(pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; + pxIterator = pxIterator->pxNextFreeBlock) { + /* Nothing to do here, just iterate to the right position. */ + } + + /* Do the block being inserted, and the block it is being inserted after + make a contiguous block of memory? */ + puc = (uint8_t*)pxIterator; + if((puc + pxIterator->xBlockSize) == (uint8_t*)pxBlockToInsert) { + pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; + pxBlockToInsert = pxIterator; + } else { + mtCOVERAGE_TEST_MARKER(); + } + + /* Do the block being inserted, and the block it is being inserted before + make a contiguous block of memory? */ + puc = (uint8_t*)pxBlockToInsert; + if((puc + pxBlockToInsert->xBlockSize) == (uint8_t*)pxIterator->pxNextFreeBlock) { + if(pxIterator->pxNextFreeBlock != pxEnd) { + /* Form one big block from the two blocks. */ + pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; + } else { + pxBlockToInsert->pxNextFreeBlock = pxEnd; + } + } else { + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; + } + + /* If the block being inserted plugged a gab, so was merged with the block + before and the block after, then it's pxNextFreeBlock pointer will have + already been set, and should not be set here as that would make it point + to itself. */ + if(pxIterator != pxBlockToInsert) { + pxIterator->pxNextFreeBlock = pxBlockToInsert; + } else { + mtCOVERAGE_TEST_MARKER(); + } +} diff --git a/furi/core/memmgr_heap.h b/furi/core/memmgr_heap.h index 2f61deb64..7d889f152 100644 --- a/furi/core/memmgr_heap.h +++ b/furi/core/memmgr_heap.h @@ -40,17 +40,9 @@ size_t memmgr_heap_get_thread_memory(FuriThreadId thread_id); */ size_t memmgr_heap_get_max_free_block(void); -typedef bool (*BlockWalker)(void* pointer, size_t size, bool used, void* context); - -/** - * @brief Walk through all heap blocks - * @warning This function will lock memory manager and may cause deadlocks if any malloc/free is called inside the callback. - * Also, printf and furi_log contains malloc calls, so do not use them. - * - * @param walker - * @param context +/** Print the address and size of all free blocks to stdout */ -void memmgr_heap_walk_blocks(BlockWalker walker, void* context); +void memmgr_heap_printf_free_blocks(void); #ifdef __cplusplus } diff --git a/furi/core/thread.c b/furi/core/thread.c index c9bf79d32..f9f73b4f7 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -276,8 +276,8 @@ void furi_thread_start(FuriThread* thread) { stack, thread, priority, - memmgr_aux_pool_alloc(sizeof(StackType_t) * stack), - memmgr_aux_pool_alloc(sizeof(StaticTask_t))); + memmgr_alloc_from_pool(sizeof(StackType_t) * stack), + memmgr_alloc_from_pool(sizeof(StaticTask_t))); } else { BaseType_t ret = xTaskCreate( furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle); diff --git a/furi/flipper.c b/furi/flipper.c index 6c7b9831a..c7ba3b4fb 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -51,17 +51,12 @@ void flipper_init(void) { FURI_LOG_I(TAG, "Startup complete"); } -PLACE_IN_SECTION("MB_MEM2") static StaticTask_t idle_task_tcb; -PLACE_IN_SECTION("MB_MEM2") static StackType_t idle_task_stack[configIDLE_TASK_STACK_DEPTH]; -PLACE_IN_SECTION("MB_MEM2") static StaticTask_t timer_task_tcb; -PLACE_IN_SECTION("MB_MEM2") static StackType_t timer_task_stack[configTIMER_TASK_STACK_DEPTH]; - void vApplicationGetIdleTaskMemory( StaticTask_t** tcb_ptr, StackType_t** stack_ptr, uint32_t* stack_size) { - *tcb_ptr = &idle_task_tcb; - *stack_ptr = idle_task_stack; + *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); + *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configIDLE_TASK_STACK_DEPTH); *stack_size = configIDLE_TASK_STACK_DEPTH; } @@ -69,7 +64,7 @@ void vApplicationGetTimerTaskMemory( StaticTask_t** tcb_ptr, StackType_t** stack_ptr, uint32_t* stack_size) { - *tcb_ptr = &timer_task_tcb; - *stack_ptr = timer_task_stack; + *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); + *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH); *stack_size = configTIMER_TASK_STACK_DEPTH; } \ No newline at end of file diff --git a/lib/SConscript b/lib/SConscript index 29c48de6d..812573932 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -13,7 +13,6 @@ env.Append( libs = env.BuildModules( [ - "tlsf", "mlib", "stm32wb", "freertos", diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 3e10ae3fe..398f25209 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -466,7 +466,7 @@ static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* return true; } - section->data = aligned_alloc(section_header->sh_addralign, section_header->sh_size); + section->data = aligned_malloc(section_header->sh_size, section_header->sh_addralign); section->size = section_header->sh_size; if(section_header->sh_type == SHT_NOBITS) { @@ -718,7 +718,7 @@ static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) { } } - free(s->fast_rel->data); + aligned_free(s->fast_rel->data); free(s->fast_rel); s->fast_rel = NULL; @@ -785,10 +785,10 @@ void elf_file_free(ELFFile* elf) { ELFSectionDict_next(it)) { const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); if(itref->value.data) { - free(itref->value.data); + aligned_free(itref->value.data); } if(itref->value.fast_rel) { - free(itref->value.fast_rel->data); + aligned_free(itref->value.fast_rel->data); free(itref->value.fast_rel); } free((void*)itref->key); diff --git a/lib/tlsf b/lib/tlsf deleted file mode 160000 index 8fc595fe2..000000000 --- a/lib/tlsf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8fc595fe223cd0b3b5d7b29eb86825e4bd38e6e8 diff --git a/lib/tlsf.scons b/lib/tlsf.scons deleted file mode 100644 index 0a8419dbd..000000000 --- a/lib/tlsf.scons +++ /dev/null @@ -1,21 +0,0 @@ -Import("env") - -env.Append( - CPPPATH=[ - "#/lib/tlsf", - ], -) - - -libenv = env.Clone(FW_LIB_NAME="tlsf") -libenv.ApplyLibFlags() - -libenv.Append( - CPPDEFINES=[], -) - -sources = [File("tlsf/tlsf.c")] - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) -libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index ef2d2fcb6..492539d46 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,62.0,, +Version,+,61.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -515,7 +515,9 @@ Function,-,acosh,double,double Function,-,acoshf,float,float Function,-,acoshl,long double,long double Function,-,acosl,long double,long double -Function,+,aligned_alloc,void*,"size_t, size_t" +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" Function,-,arc4random,__uint32_t, Function,-,arc4random_buf,void,"void*, size_t" Function,-,arc4random_uniform,__uint32_t,__uint32_t @@ -1982,8 +1984,7 @@ Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" Function,+,memcpy,void*,"void*, const void*, size_t" Function,-,memmem,void*,"const void*, size_t, const void*, size_t" -Function,+,memmgr_aux_pool_alloc,void*,size_t -Function,+,memmgr_aux_pool_get_free,size_t, +Function,-,memmgr_alloc_from_pool,void*,size_t Function,+,memmgr_get_free_heap,size_t, Function,+,memmgr_get_minimum_free_heap,size_t, Function,+,memmgr_get_total_heap,size_t, @@ -1991,7 +1992,8 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_get_max_free_block,size_t, Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId -Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*" +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, Function,-,memmgr_pool_get_max_block,size_t, Function,+,memmove,void*,"void*, const void*, size_t" Function,-,mempcpy,void*,"void*, const void*, size_t" diff --git a/targets/f18/target.json b/targets/f18/target.json index a61c1373e..43e9254cd 100644 --- a/targets/f18/target.json +++ b/targets/f18/target.json @@ -13,7 +13,6 @@ "print", "flipper18", "furi", - "tlsf", "freertos", "stm32wb", "hwdrivers", @@ -69,4 +68,4 @@ "ibutton", "infrared" ] -} \ No newline at end of file +} diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 1f959f0c7..e209023b5 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,62.0,, +Version,+,61.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -586,7 +586,9 @@ Function,-,acosh,double,double Function,-,acoshf,float,float Function,-,acoshl,long double,long double Function,-,acosl,long double,long double -Function,+,aligned_alloc,void*,"size_t, size_t" +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" Function,-,arc4random,__uint32_t, Function,-,arc4random_buf,void,"void*, size_t" Function,-,arc4random_uniform,__uint32_t,__uint32_t @@ -2392,8 +2394,7 @@ Function,+,memchr,void*,"const void*, int, size_t" Function,+,memcmp,int,"const void*, const void*, size_t" Function,+,memcpy,void*,"void*, const void*, size_t" Function,-,memmem,void*,"const void*, size_t, const void*, size_t" -Function,+,memmgr_aux_pool_alloc,void*,size_t -Function,+,memmgr_aux_pool_get_free,size_t, +Function,-,memmgr_alloc_from_pool,void*,size_t Function,+,memmgr_get_free_heap,size_t, Function,+,memmgr_get_minimum_free_heap,size_t, Function,+,memmgr_get_total_heap,size_t, @@ -2401,7 +2402,8 @@ Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId Function,+,memmgr_heap_get_max_free_block,size_t, Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId -Function,+,memmgr_heap_walk_blocks,void,"BlockWalker, void*" +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, Function,-,memmgr_pool_get_max_block,size_t, Function,+,memmove,void*,"void*, const void*, size_t" Function,-,mempcpy,void*,"void*, const void*, size_t" diff --git a/targets/f7/fatfs/sector_cache.c b/targets/f7/fatfs/sector_cache.c index df86cb7f1..319dd2173 100644 --- a/targets/f7/fatfs/sector_cache.c +++ b/targets/f7/fatfs/sector_cache.c @@ -19,7 +19,7 @@ static SectorCache* cache = NULL; void sector_cache_init(void) { if(cache == NULL) { - cache = memmgr_aux_pool_alloc(sizeof(SectorCache)); + cache = memmgr_alloc_from_pool(sizeof(SectorCache)); } if(cache != NULL) { diff --git a/targets/f7/src/update.c b/targets/f7/src/update.c index 261adb5ca..e6cb4aabe 100644 --- a/targets/f7/src/update.c +++ b/targets/f7/src/update.c @@ -78,21 +78,21 @@ static bool flipper_update_load_stage(const FuriString* work_dir, UpdateManifest furi_string_free(loader_img_path); void* img = malloc(stat.fsize); - uint32_t read_total = 0; - uint16_t read_current = 0; + uint32_t bytes_read = 0; const uint16_t MAX_READ = 0xFFFF; uint32_t crc = 0; do { - if(f_read(&file, img + read_total, MAX_READ, &read_current) != FR_OK) { //-V769 + uint16_t size_read = 0; + if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { //-V769 break; } - crc = crc32_calc_buffer(crc, img + read_total, read_current); - read_total += read_current; - } while(read_current == MAX_READ); + crc = crc32_calc_buffer(crc, img + bytes_read, size_read); + bytes_read += size_read; + } while(bytes_read == MAX_READ); do { - if((read_total != stat.fsize) || (crc != manifest->staged_loader_crc)) { + if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { break; } diff --git a/targets/f7/target.json b/targets/f7/target.json index caa3f58ee..25872198b 100644 --- a/targets/f7/target.json +++ b/targets/f7/target.json @@ -22,7 +22,6 @@ "print", "flipper7", "furi", - "tlsf", "freertos", "stm32wb", "hwdrivers", @@ -56,4 +55,4 @@ "bit_lib", "datetime" ] -} \ No newline at end of file +}