diff --git a/connectivity/libraries/nanostack-libservice/Makefile b/connectivity/libraries/nanostack-libservice/Makefile index 24fd3418e9..390ff1f777 100644 --- a/connectivity/libraries/nanostack-libservice/Makefile +++ b/connectivity/libraries/nanostack-libservice/Makefile @@ -5,6 +5,7 @@ source/libip6string/ip6tos.c \ source/libip6string/stoip6.c \ source/libList/ns_list.c \ source/nsdynmemLIB/nsdynmemLIB.c \ +source/nsdynmemtracker/nsdynmem_tracker_lib.c \ source/nvmHelper/ns_nvm_helper.c \ LIB := libservice.a diff --git a/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmemLIB.h b/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmemLIB.h index bf0056ba1a..ee91bfc8b1 100644 --- a/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmemLIB.h +++ b/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmemLIB.h @@ -37,6 +37,9 @@ extern "C" { typedef size_t ns_mem_block_size_t; //external interface unsigned heap block size type typedef size_t ns_mem_heap_size_t; //total heap size type. +// Can be used to enable tracking of dynamic memory allocations +#include "nsdynmem_tracker.h" + /*! * \enum heap_fail_t * \brief Dynamically heap system failure call back event types. @@ -64,7 +67,6 @@ typedef struct mem_stat_t { uint32_t heap_alloc_fail_cnt; /**< Counter for Heap allocation fail. */ } mem_stat_t; - typedef struct ns_mem_book ns_mem_book_t; /** @@ -99,7 +101,9 @@ extern int ns_dyn_mem_region_add(void *region_ptr, ns_mem_heap_size_t region_siz * \return 0, Free OK * \return <0, Free Fail */ +#if NSDYNMEM_TRACKER_ENABLED!=1 extern void ns_dyn_mem_free(void *heap_ptr); +#endif /** * \brief Allocate temporary data. @@ -111,7 +115,9 @@ extern void ns_dyn_mem_free(void *heap_ptr); * \return 0, Allocate Fail * \return >0, Pointer to allocated data sector. */ +#if NSDYNMEM_TRACKER_ENABLED!=1 extern void *ns_dyn_mem_temporary_alloc(ns_mem_block_size_t alloc_size); +#endif /** * \brief Allocate long period data. @@ -123,7 +129,9 @@ extern void *ns_dyn_mem_temporary_alloc(ns_mem_block_size_t alloc_size); * \return 0, Allocate Fail * \return >0, Pointer to allocated data sector. */ +#if NSDYNMEM_TRACKER_ENABLED!=1 extern void *ns_dyn_mem_alloc(ns_mem_block_size_t alloc_size); +#endif /** * \brief Get pointer to the current mem_stat_t set via ns_dyn_mem_init. diff --git a/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker.h b/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker.h new file mode 100644 index 0000000000..c0fccdf97e --- /dev/null +++ b/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file nsdynmem_tracker.h + * \brief Dynamical Memory Tracker definitions to override the default NS dynamic memory functionality + * Provides tracking and tracing of dynamic memory blocks + */ + +#ifndef NSDYNMEM_TRACKER_H_ +#define NSDYNMEM_TRACKER_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#if NSDYNMEM_TRACKER_ENABLED==1 + +#define ns_dyn_mem_free(block) ns_dyn_mem_tracker_dyn_mem_free(block, __func__, __LINE__) +#define ns_dyn_mem_temporary_alloc(alloc_size) ns_dyn_mem_tracker_dyn_mem_temporary_alloc(alloc_size, __func__, __LINE__) +#define ns_dyn_mem_alloc(alloc_size) ns_dyn_mem_tracker_dyn_mem_alloc(alloc_size, __func__, __LINE__) + +void *ns_dyn_mem_tracker_dyn_mem_alloc(ns_mem_heap_size_t alloc_size, const char *function, uint32_t line); +void *ns_dyn_mem_tracker_dyn_mem_temporary_alloc(ns_mem_heap_size_t alloc_size, const char *function, uint32_t line); +void ns_dyn_mem_tracker_dyn_mem_free(void *block, const char *function, uint32_t line); + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* NSDYNMEM_TRACKER_H_ */ + + diff --git a/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker_lib.h b/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker_lib.h new file mode 100644 index 0000000000..aa76f390bf --- /dev/null +++ b/connectivity/libraries/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker_lib.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * \file nsdynmem_tracker_lib.h + * \brief Dynamical Memory Tracker library API + * Provides tracking and tracing of dynamic memory blocks + */ + +#ifndef NSDYNMEM_TRACKER_LIB_H_ +#define NSDYNMEM_TRACKER_LIB_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#if NSDYNMEM_TRACKER_ENABLED==1 + +// Memory block structure with caller information +typedef struct ns_dyn_mem_tracker_lib_mem_blocks_s { + void *block; /**< Allocated memory block */ + void *caller_addr; /**< Caller address */ + uint32_t size; /**< Allocation size */ + uint32_t total_size; /**< Total allocation size for all allocations */ + uint32_t lifetime; /**< Memory block lifetime in steps (e.g. seconds) */ + uint32_t ref_count; /**< Reference count */ + const char *function; /**< Caller function */ + uint16_t line; /**< Caller line in module */ + bool permanent : 1; /**< Permanent memory block */ + bool permanent_printed : 1; /**< Permanent memory block printed */ +} ns_dyn_mem_tracker_lib_mem_blocks_t; + +// Extended memory block structure that is used if same caller allocates multiple memory blocks +typedef struct ns_dyn_mem_tracker_lib_mem_blocks_ext_s { + void *block; /**< Allocated memory block */ + void *caller_addr; /**< Caller address */ + uint32_t size; /**< Allocation size */ +} ns_dyn_mem_tracker_lib_mem_blocks_ext_t; + +// Allocator information structure +typedef struct ns_dyn_mem_tracker_lib_allocators_s { + void *caller_addr; /**< Caller address */ + uint32_t alloc_count; /**< Number of allocations */ + uint32_t total_memory; /**< Total memory used by allocations */ + uint32_t min_lifetime; /**< Shortest lifetime among the allocations */ + const char *function; /**< Function name string */ + uint16_t line; /**< Module line */ +} ns_dyn_mem_tracker_lib_allocators_t; + +// Memory block array allocator / array size increase allocator +typedef ns_dyn_mem_tracker_lib_mem_blocks_t *ns_dyn_mem_tracker_lib_alloc_mem_blocks(ns_dyn_mem_tracker_lib_mem_blocks_t *blocks, uint16_t *mem_blocks_count); +// Extended memory block array allocator / array size increase allocator +typedef ns_dyn_mem_tracker_lib_mem_blocks_ext_t *ns_dyn_mem_tracker_lib_alloc_mem_blocks_ext(ns_dyn_mem_tracker_lib_mem_blocks_ext_t *blocks, uint32_t *mem_blocks_count); +// Extended memory block array index hash function to get memory block (allocation/search start) index from block address +typedef uint32_t ns_dyn_mem_tracker_lib_mem_block_index_hash(void *block, uint32_t ext_mem_blocks_count); + +typedef struct ns_dyn_mem_tracker_lib_conf_s { + ns_dyn_mem_tracker_lib_mem_blocks_t *mem_blocks; /**< Memory blocks array, if NULL calls allocator on init */ + ns_dyn_mem_tracker_lib_mem_blocks_ext_t *ext_mem_blocks; /**< Extended memory blocks array, if NULL calls allocator on init */ + ns_dyn_mem_tracker_lib_allocators_t *top_allocators; /**< Top allocators array */ + ns_dyn_mem_tracker_lib_allocators_t *permanent_allocators; /**< Permanent allocators */ + ns_dyn_mem_tracker_lib_allocators_t *to_permanent_allocators; /**< To permanent allocators */ + ns_dyn_mem_tracker_lib_allocators_t *max_snap_shot_allocators; /**< Snap shot of maximum memory used by allocators */ + ns_dyn_mem_tracker_lib_alloc_mem_blocks *alloc_mem_blocks; /**< Memory block array allocator / array size increase allocator */ + ns_dyn_mem_tracker_lib_alloc_mem_blocks_ext *ext_alloc_mem_blocks; /**< Extended memory block array allocator / array size increase allocator */ + ns_dyn_mem_tracker_lib_mem_block_index_hash *block_index_hash; /**< Hash function to get memory block index from block address */ + uint32_t allocated_memory; /**< Currently allocated memory */ + uint16_t mem_blocks_count; /**< Number of entries in memory blocks array */ + uint32_t ext_mem_blocks_count; /**< Number of entries in extended memory blocks array */ + uint16_t last_mem_block_index; /**< Last memory block in memory blocks array */ + uint16_t top_allocators_count; /**< Top allocators array count */ + uint16_t permanent_allocators_count; /**< Permanent allocators array count */ + uint16_t to_permanent_allocators_count; /**< To permanent allocators array count */ + uint16_t max_snap_shot_allocators_count; /**< Snap shot of maximum memory used by allocators array count */ + uint16_t to_permanent_steps_count; /**< How many steps before moving block to permanent allocators list */ +} ns_dyn_mem_tracker_lib_conf_t; + +int8_t ns_dyn_mem_tracker_lib_alloc(ns_dyn_mem_tracker_lib_conf_t *conf, void *caller_addr, const char *function, uint32_t line, void *block, uint32_t alloc_size); +int8_t ns_dyn_mem_tracker_lib_free(ns_dyn_mem_tracker_lib_conf_t *conf, void *caller_addr, const char *function, uint32_t line, void *block); +void ns_dyn_mem_tracker_lib_step(ns_dyn_mem_tracker_lib_conf_t *conf); +int8_t ns_dyn_mem_tracker_lib_allocator_lists_update(ns_dyn_mem_tracker_lib_conf_t *conf); +void ns_dyn_mem_tracker_lib_max_snap_shot_update(ns_dyn_mem_tracker_lib_conf_t *conf); + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* NSDYNMEM_TRACKER_LIB_H_ */ + + diff --git a/connectivity/libraries/nanostack-libservice/mbed_lib.json b/connectivity/libraries/nanostack-libservice/mbed_lib.json index ed51687e70..910933b45e 100644 --- a/connectivity/libraries/nanostack-libservice/mbed_lib.json +++ b/connectivity/libraries/nanostack-libservice/mbed_lib.json @@ -1,6 +1,11 @@ { "name": "nanostack-libservice", + "macros": ["NSDYNMEM_TRACKER_ENABLED=MBED_CONF_NANOSTACK_LIBSERVICE_NSDYNMEM_TRACKER_ENABLED"], "config": { - "present": 1 + "present": 1, + "nsdynmem-tracker-enabled": { + "help": "Use to enable dynamic memory tracker", + "value": 0 + } } } diff --git a/connectivity/libraries/nanostack-libservice/source/nsdynmemLIB/nsdynmemLIB.c b/connectivity/libraries/nanostack-libservice/source/nsdynmemLIB/nsdynmemLIB.c index 5f913d5758..99ba624e05 100644 --- a/connectivity/libraries/nanostack-libservice/source/nsdynmemLIB/nsdynmemLIB.c +++ b/connectivity/libraries/nanostack-libservice/source/nsdynmemLIB/nsdynmemLIB.c @@ -15,6 +15,7 @@ */ #include #include +#undef NSDYNMEM_TRACKER_ENABLED #include "nsdynmemLIB.h" #include "platform/arm_hal_interrupt.h" #include @@ -47,6 +48,7 @@ struct ns_mem_book { ns_mem_heap_size_t temporary_alloc_heap_limit; /* Amount of reserved heap temporary alloc can't exceed */ }; +static mem_stat_t default_stats; static ns_mem_book_t *default_book; // heap pointer for original "ns_" API use // size of a hole_t in our word units @@ -162,12 +164,14 @@ ns_mem_book_t *ns_mem_init(void *heap, ns_mem_heap_size_t h_size, ns_list_init(&book->holes_list); ns_list_add_to_start(&book->holes_list, hole_from_block_start(book->heap_main[0])); - book->mem_stat_info_ptr = info_ptr; - //RESET Memory by Hea Len if (info_ptr) { - memset(book->mem_stat_info_ptr, 0, sizeof(mem_stat_t)); - book->mem_stat_info_ptr->heap_sector_size = book->heap_size; + book->mem_stat_info_ptr = info_ptr; + } else { + book->mem_stat_info_ptr = &default_stats; } + //RESET Memory by Heap Len + memset(book->mem_stat_info_ptr, 0, sizeof(mem_stat_t)); + book->mem_stat_info_ptr->heap_sector_size = book->heap_size; book->temporary_alloc_heap_limit = book->heap_size / 100 * (100 - TEMPORARY_ALLOC_FREE_HEAP_THRESHOLD); #endif //There really is no support to standard malloc in this library anymore @@ -234,9 +238,7 @@ int ns_mem_region_add(ns_mem_book_t *book, void *region_ptr, ns_mem_heap_size_t // adjust total heap size with new hole book->heap_size += region_size; - if (book->mem_stat_info_ptr) { - book->mem_stat_info_ptr->heap_sector_size = book->heap_size; - } + book->mem_stat_info_ptr->heap_sector_size = book->heap_size; // adjust temporary allocation limits to match new heap book->temporary_alloc_heap_limit = book->heap_size / 100 * (100 - TEMPORARY_ALLOC_FREE_HEAP_THRESHOLD); @@ -265,8 +267,8 @@ int ns_mem_set_temporary_alloc_free_heap_threshold(ns_mem_book_t *book, uint8_t #ifndef STANDARD_MALLOC ns_mem_heap_size_t heap_limit = 0; - if (!book || !book->mem_stat_info_ptr) { - // no book or mem_stats + if (!book) { + // no book return -1; } @@ -304,25 +306,25 @@ extern int ns_dyn_mem_set_temporary_alloc_free_heap_threshold(uint8_t free_heap_ #ifndef STANDARD_MALLOC static void dev_stat_update(mem_stat_t *mem_stat_info_ptr, mem_stat_update_t type, ns_mem_block_size_t size) { - if (mem_stat_info_ptr) { - switch (type) { - case DEV_HEAP_ALLOC_OK: - mem_stat_info_ptr->heap_sector_alloc_cnt++; - mem_stat_info_ptr->heap_sector_allocated_bytes += size; - if (mem_stat_info_ptr->heap_sector_allocated_bytes_max < mem_stat_info_ptr->heap_sector_allocated_bytes) { - mem_stat_info_ptr->heap_sector_allocated_bytes_max = mem_stat_info_ptr->heap_sector_allocated_bytes; - } - mem_stat_info_ptr->heap_alloc_total_bytes += size; - break; - case DEV_HEAP_ALLOC_FAIL: - mem_stat_info_ptr->heap_alloc_fail_cnt++; - break; - case DEV_HEAP_FREE: - mem_stat_info_ptr->heap_sector_alloc_cnt--; - mem_stat_info_ptr->heap_sector_allocated_bytes -= size; - break; - } + + switch (type) { + case DEV_HEAP_ALLOC_OK: + mem_stat_info_ptr->heap_sector_alloc_cnt++; + mem_stat_info_ptr->heap_sector_allocated_bytes += size; + if (mem_stat_info_ptr->heap_sector_allocated_bytes_max < mem_stat_info_ptr->heap_sector_allocated_bytes) { + mem_stat_info_ptr->heap_sector_allocated_bytes_max = mem_stat_info_ptr->heap_sector_allocated_bytes; + } + mem_stat_info_ptr->heap_alloc_total_bytes += size; + break; + case DEV_HEAP_ALLOC_FAIL: + mem_stat_info_ptr->heap_alloc_fail_cnt++; + break; + case DEV_HEAP_FREE: + mem_stat_info_ptr->heap_sector_alloc_cnt--; + mem_stat_info_ptr->heap_sector_allocated_bytes -= size; + break; } + } static ns_mem_word_size_t convert_allocation_size(ns_mem_book_t *book, ns_mem_block_size_t requested_bytes) @@ -363,7 +365,7 @@ static void *ns_mem_internal_alloc(ns_mem_book_t *book, const ns_mem_block_size_ return NULL; } - if (book->mem_stat_info_ptr && direction == 1) { + if (direction == 1) { if (book->mem_stat_info_ptr->heap_sector_allocated_bytes > book->temporary_alloc_heap_limit) { /* Not enough heap for temporary memory allocation */ dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_ALLOC_FAIL, 0); @@ -441,14 +443,14 @@ static void *ns_mem_internal_alloc(ns_mem_book_t *book, const ns_mem_block_size_ block_ptr[1 + data_size] = data_size; done: - if (book->mem_stat_info_ptr) { - if (block_ptr) { - //Update Allocate OK - dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_ALLOC_OK, (data_size + 2) * sizeof(ns_mem_word_size_t)); - } else { - //Update Allocate Fail, second parameter is used for stats - dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_ALLOC_FAIL, 0); - } + + + if (block_ptr) { + //Update Allocate OK + dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_ALLOC_OK, (data_size + 2) * sizeof(ns_mem_word_size_t)); + } else { + //Update Allocate Fail, second parameter is used for stats + dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_ALLOC_FAIL, 0); } platform_exit_critical(); @@ -619,10 +621,8 @@ void ns_mem_free(ns_mem_book_t *book, void *block) heap_failure(book, NS_DYN_MEM_HEAP_SECTOR_CORRUPTED); } else { ns_mem_free_and_merge_with_adjacent_blocks(book, ptr, size); - if (book->mem_stat_info_ptr) { - //Update Free Counter - dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_FREE, (size + 2) * sizeof(ns_mem_word_size_t)); - } + //Update Free Counter + dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_FREE, (size + 2) * sizeof(ns_mem_word_size_t)); } } diff --git a/connectivity/libraries/nanostack-libservice/source/nsdynmemtracker/nsdynmem_tracker_lib.c b/connectivity/libraries/nanostack-libservice/source/nsdynmemtracker/nsdynmem_tracker_lib.c new file mode 100644 index 0000000000..9c2bcdfda4 --- /dev/null +++ b/connectivity/libraries/nanostack-libservice/source/nsdynmemtracker/nsdynmem_tracker_lib.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2020 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "ns_types.h" +#include "nsdynmem_tracker_lib.h" +#include "platform/arm_hal_interrupt.h" +#include "nsdynmemLIB.h" + +#if NSDYNMEM_TRACKER_ENABLED==1 + +static int8_t ns_dyn_mem_tracker_lib_find_free_index(ns_dyn_mem_tracker_lib_conf_t *conf, uint16_t *index); +static int8_t ns_dyn_mem_tracker_lib_find_caller_index(ns_dyn_mem_tracker_lib_conf_t *conf, void *caller_addr, uint16_t *caller_index); +static int8_t ns_dyn_mem_tracker_lib_find_block_index(ns_dyn_mem_tracker_lib_conf_t *conf, void *block, uint16_t *block_index); +static int8_t ns_dyn_mem_tracker_lib_ext_find_free_index(ns_dyn_mem_tracker_lib_conf_t *conf, uint32_t start_index, uint32_t *free_index); +static void ns_dyn_mem_tracker_lib_permanent_printed_value_set(ns_dyn_mem_tracker_lib_conf_t *conf, void *caller_addr, bool new_value); +static int8_t ns_dyn_mem_tracker_lib_ext_find_block_index(ns_dyn_mem_tracker_lib_conf_t *conf, void *block, uint32_t start_index, uint32_t *block_index); + +int8_t ns_dyn_mem_tracker_lib_alloc(ns_dyn_mem_tracker_lib_conf_t *conf, void *caller_addr, const char *function, uint32_t line, void *block, uint32_t alloc_size) +{ + // Allocation failed + if (block == NULL) { + return 0; + } + + platform_enter_critical(); + + // If dynamic memory blocks are not set, calls allocator + if (conf->mem_blocks == NULL) { + conf->mem_blocks = conf->alloc_mem_blocks(conf->mem_blocks, &conf->mem_blocks_count); + if (conf->mem_blocks == NULL) { + platform_exit_critical(); + return -1; + } + } + + uint16_t caller_index = 0; + if (ns_dyn_mem_tracker_lib_find_caller_index(conf, caller_addr, &caller_index) >= 0) { + if (conf->ext_mem_blocks == NULL) { + conf->ext_mem_blocks = conf->ext_alloc_mem_blocks(conf->ext_mem_blocks, &conf->ext_mem_blocks_count); + if (conf->ext_mem_blocks == NULL) { + platform_exit_critical(); + return -1; + } + } + + uint32_t free_index = 0; + uint32_t start_index = 0; + if (conf->block_index_hash != NULL) { + start_index = conf->block_index_hash(block, conf->ext_mem_blocks_count); + } + if (ns_dyn_mem_tracker_lib_ext_find_free_index(conf, start_index, &free_index) < 0) { + conf->ext_mem_blocks = conf->ext_alloc_mem_blocks(conf->ext_mem_blocks, &conf->ext_mem_blocks_count); + if (conf->ext_mem_blocks == NULL) { + platform_exit_critical(); + return -1; + } + if (conf->block_index_hash != NULL) { + start_index = conf->block_index_hash(block, conf->ext_mem_blocks_count); + } + if (ns_dyn_mem_tracker_lib_ext_find_free_index(conf, start_index, &free_index) < 0) { + platform_exit_critical(); + return -1; + } + } + + // Updates memory blocks array entry + conf->mem_blocks[caller_index].ref_count++; + conf->mem_blocks[caller_index].total_size += alloc_size; + conf->mem_blocks[caller_index].lifetime = 0; + conf->mem_blocks[caller_index].permanent = false; + conf->mem_blocks[caller_index].permanent_printed = false; + + conf->ext_mem_blocks[free_index].block = block; + conf->ext_mem_blocks[free_index].caller_addr = caller_addr; + conf->ext_mem_blocks[free_index].size = alloc_size; + + conf->allocated_memory += alloc_size; + + platform_exit_critical(); + return 0; + } + + uint16_t free_index = 0; + if (ns_dyn_mem_tracker_lib_find_free_index(conf, &free_index) < 0) { + conf->mem_blocks = conf->alloc_mem_blocks(conf->mem_blocks, &conf->mem_blocks_count); + if (conf->mem_blocks == NULL || (ns_dyn_mem_tracker_lib_find_free_index(conf, &free_index) < 0)) { + platform_exit_critical(); + return -1; + } + } + + conf->mem_blocks[free_index].block = block; + conf->mem_blocks[free_index].caller_addr = caller_addr; + conf->mem_blocks[free_index].size = alloc_size; + conf->mem_blocks[free_index].total_size = alloc_size; + conf->mem_blocks[free_index].lifetime = 0; + conf->mem_blocks[free_index].ref_count = 1; + conf->mem_blocks[free_index].function = function; + conf->mem_blocks[free_index].line = line; + conf->mem_blocks[free_index].permanent = false; + conf->mem_blocks[free_index].permanent_printed = false; + + if (free_index > conf->last_mem_block_index) { + conf->last_mem_block_index = free_index; + } + + conf->allocated_memory += alloc_size; + + platform_exit_critical(); + + return 0; +} + +int8_t ns_dyn_mem_tracker_lib_free(ns_dyn_mem_tracker_lib_conf_t *conf, void *caller_addr, const char *function, uint32_t line, void *block) +{ + (void) function; + (void) line; + (void) caller_addr; + + // No memory block or no allocations made + if (block == NULL || conf->mem_blocks == NULL) { + return 0; + } + + platform_enter_critical(); + + uint16_t block_index = 0; + if (ns_dyn_mem_tracker_lib_find_block_index(conf, block, &block_index) >= 0) { + // If last block for allocator clears the allocator + if (conf->mem_blocks[block_index].ref_count <= 1) { + conf->mem_blocks[block_index].ref_count = 0; + conf->mem_blocks[block_index].caller_addr = NULL; + conf->mem_blocks[block_index].total_size = 0; + conf->mem_blocks[block_index].function = NULL; + conf->mem_blocks[block_index].line = 0; + } else { + // Other blocks exists + conf->mem_blocks[block_index].ref_count--; + conf->mem_blocks[block_index].total_size -= conf->mem_blocks[block_index].size; + } + + conf->allocated_memory -= conf->mem_blocks[block_index].size; + + // Clears block specific fields + conf->mem_blocks[block_index].block = NULL; + conf->mem_blocks[block_index].size = 0; + // Resets lifetime and permanent settings + conf->mem_blocks[block_index].lifetime = 0; + conf->mem_blocks[block_index].permanent = false; + conf->mem_blocks[block_index].permanent_printed = false; + + platform_exit_critical(); + return 0; + } + + if (conf->ext_mem_blocks == NULL) { + platform_exit_critical(); + return -1; + } + + uint32_t ext_block_index = 0; + uint32_t start_index = 0; + if (conf->block_index_hash != NULL) { + start_index = conf->block_index_hash(block, conf->ext_mem_blocks_count); + } + if (ns_dyn_mem_tracker_lib_ext_find_block_index(conf, block, start_index, &ext_block_index) < 0) { + platform_exit_critical(); + return -1; + } + + void *ext_caller_addr = conf->ext_mem_blocks[ext_block_index].caller_addr; + + uint16_t caller_index; + if (ns_dyn_mem_tracker_lib_find_caller_index(conf, ext_caller_addr, &caller_index) < 0) { + platform_exit_critical(); + return -1; + } + + conf->mem_blocks[caller_index].ref_count--; + conf->mem_blocks[caller_index].total_size -= conf->ext_mem_blocks[ext_block_index].size; + + conf->allocated_memory -= conf->ext_mem_blocks[ext_block_index].size; + + // Clears extended block + conf->ext_mem_blocks[ext_block_index].block = NULL; + conf->ext_mem_blocks[ext_block_index].caller_addr = NULL; + conf->ext_mem_blocks[ext_block_index].size = 0; + + // Resets lifetime and permanent settings + conf->mem_blocks[block_index].lifetime = 0; + conf->mem_blocks[block_index].permanent = false; + conf->mem_blocks[block_index].permanent_printed = false; + + // If last block for allocator clears the allocator + if (conf->mem_blocks[block_index].ref_count == 0) { + conf->mem_blocks[block_index].block = NULL; + conf->mem_blocks[block_index].caller_addr = NULL; + conf->mem_blocks[block_index].size = 0; + conf->mem_blocks[block_index].total_size = 0; + conf->mem_blocks[block_index].function = NULL; + conf->mem_blocks[block_index].line = 0; + } + + platform_exit_critical(); + + return 0; +} + +void ns_dyn_mem_tracker_lib_step(ns_dyn_mem_tracker_lib_conf_t *conf) +{ + platform_enter_critical(); + + if (conf->mem_blocks_count != 0) { + for (uint32_t index = 0; index <= conf->last_mem_block_index; index++) { + if (conf->mem_blocks[index].block != NULL) { + conf->mem_blocks[index].lifetime++; + } + } + } + + platform_exit_critical(); +} + +int8_t ns_dyn_mem_tracker_lib_allocator_lists_update(ns_dyn_mem_tracker_lib_conf_t *conf) +{ + platform_enter_critical(); + + ns_dyn_mem_tracker_lib_mem_blocks_t *blocks = conf->mem_blocks; + ns_dyn_mem_tracker_lib_allocators_t *top_allocators = conf->top_allocators; + ns_dyn_mem_tracker_lib_allocators_t *permanent_allocators = conf->permanent_allocators; + ns_dyn_mem_tracker_lib_allocators_t *to_permanent_allocators = conf->to_permanent_allocators; + + uint16_t top_allocators_count = conf->top_allocators_count; + uint16_t permanent_allocators_count = conf->permanent_allocators_count; + uint16_t to_permanent_allocators_count = conf->to_permanent_allocators_count; + + memset(top_allocators, 0, top_allocators_count * sizeof(ns_dyn_mem_tracker_lib_allocators_t)); + memset(permanent_allocators, 0, permanent_allocators_count * sizeof(ns_dyn_mem_tracker_lib_allocators_t)); + memset(to_permanent_allocators, 0, to_permanent_allocators_count * sizeof(ns_dyn_mem_tracker_lib_allocators_t)); + + // Maximum set as permanent in one go + uint8_t to_permanent_count = 0; + + // Maximum to print of permanent entries + uint8_t permanent_count = 0; + + for (uint32_t index = 0; index <= conf->last_mem_block_index; index++) { + if (blocks[index].block != NULL) { + void *caller_addr = blocks[index].caller_addr; + + // Checks if caller address has already been counted + bool next = false; + for (uint32_t list_index = 0; list_index < top_allocators_count; list_index++) { + if (top_allocators[list_index].caller_addr == caller_addr) { + next = true; + break; + } + } + if (next) { + // Already on list, continue + continue; + } + + // Checks whether all reference are marked permanent + if (blocks[index].permanent) { + if (!blocks[index].permanent_printed && permanent_count < permanent_allocators_count) { + permanent_allocators[permanent_count].caller_addr = caller_addr; + permanent_allocators[permanent_count].alloc_count = blocks[index].ref_count; + permanent_allocators[permanent_count].total_memory = blocks[index].total_size; + permanent_allocators[permanent_count].min_lifetime = blocks[index].lifetime; + permanent_allocators[permanent_count].function = blocks[index].function; + permanent_allocators[permanent_count].line = blocks[index].line; + + permanent_count++; + blocks[index].permanent_printed = true; + } + continue; + } else { + // Checks whether lifetime threshold has been reached, traces and skips + if (blocks[index].lifetime > conf->to_permanent_steps_count && to_permanent_count < to_permanent_allocators_count) { + blocks[index].permanent = true; + + to_permanent_allocators[to_permanent_count].caller_addr = caller_addr; + to_permanent_allocators[to_permanent_count].alloc_count = blocks[index].ref_count; + to_permanent_allocators[to_permanent_count].total_memory = blocks[index].total_size; + to_permanent_allocators[to_permanent_count].min_lifetime = blocks[index].lifetime; + to_permanent_allocators[to_permanent_count].function = blocks[index].function; + to_permanent_allocators[to_permanent_count].line = blocks[index].line; + + to_permanent_count++; + continue; + } + } + + // Add to list if allocation count is larger than entry on the list + for (uint16_t list_index = 0; list_index < top_allocators_count; list_index++) { + if (blocks[index].ref_count >= top_allocators[list_index].alloc_count) { + if (list_index != (top_allocators_count - 1)) { + uint8_t index_count = (top_allocators_count - list_index - 1); + uint32_t size = index_count * sizeof(ns_dyn_mem_tracker_lib_allocators_t); + memmove(&top_allocators[list_index + 1], &top_allocators[list_index], size); + } + top_allocators[list_index].caller_addr = caller_addr; + top_allocators[list_index].alloc_count = blocks[index].ref_count; + top_allocators[list_index].total_memory = blocks[index].total_size; + top_allocators[list_index].min_lifetime = blocks[index].lifetime; + top_allocators[list_index].function = blocks[index].function; + top_allocators[list_index].line = blocks[index].line; + break; + } + } + } + } + + if (permanent_count < permanent_allocators_count) { + ns_dyn_mem_tracker_lib_permanent_printed_value_set(conf, NULL, false); + } + + platform_exit_critical(); + + return 0; +} + +void ns_dyn_mem_tracker_lib_max_snap_shot_update(ns_dyn_mem_tracker_lib_conf_t *conf) +{ + platform_enter_critical(); + + ns_dyn_mem_tracker_lib_mem_blocks_t *blocks = conf->mem_blocks; + ns_dyn_mem_tracker_lib_allocators_t *max_snap_shot_allocators = conf->max_snap_shot_allocators; + + uint16_t max_snap_shot_allocators_count = conf->max_snap_shot_allocators_count; + + memset(max_snap_shot_allocators, 0, max_snap_shot_allocators_count * sizeof(ns_dyn_mem_tracker_lib_allocators_t)); + + for (uint32_t index = 0; index <= conf->last_mem_block_index; index++) { + if (blocks[index].block != NULL) { + void *caller_addr = blocks[index].caller_addr; + + // Checks if caller address has already been counted + bool next = false; + for (uint16_t list_index = 0; list_index < max_snap_shot_allocators_count; list_index++) { + if (max_snap_shot_allocators[list_index].caller_addr == caller_addr) { + next = true; + break; + } + } + if (next) { + // Already on list, continue + continue; + } + + // Add to list if allocation count is larger than entry on the list + for (uint16_t list_index = 0; list_index < max_snap_shot_allocators_count; list_index++) { + if (blocks[index].total_size >= max_snap_shot_allocators[list_index].total_memory) { + if (list_index != (max_snap_shot_allocators_count - 1)) { + uint8_t index_count = (max_snap_shot_allocators_count - list_index - 1); + uint32_t size = index_count * sizeof(ns_dyn_mem_tracker_lib_allocators_t); + memmove(&max_snap_shot_allocators[list_index + 1], &max_snap_shot_allocators[list_index], size); + } + max_snap_shot_allocators[list_index].caller_addr = blocks[index].caller_addr; + max_snap_shot_allocators[list_index].alloc_count = blocks[index].ref_count; + max_snap_shot_allocators[list_index].total_memory = blocks[index].total_size; + max_snap_shot_allocators[list_index].min_lifetime = blocks[index].lifetime; + max_snap_shot_allocators[list_index].function = blocks[index].function; + max_snap_shot_allocators[list_index].line = blocks[index].line; + break; + } + } + } + } + + platform_exit_critical(); +} + +static void ns_dyn_mem_tracker_lib_permanent_printed_value_set(ns_dyn_mem_tracker_lib_conf_t *conf, void *caller_addr, bool new_value) +{ + /* Search for all the references to the caller address from the allocation info and + set block permanent value */ + for (uint16_t search_index = 0; search_index <= conf->last_mem_block_index; search_index++) { + if (caller_addr == NULL || conf->mem_blocks[search_index].caller_addr == caller_addr) { + conf->mem_blocks[search_index].permanent_printed = new_value; + } + } +} + +static int8_t ns_dyn_mem_tracker_lib_find_free_index(ns_dyn_mem_tracker_lib_conf_t *conf, uint16_t *free_index) +{ + for (uint16_t index = 0; index < conf->mem_blocks_count; index++) { + if (conf->mem_blocks[index].caller_addr == NULL) { + *free_index = index; + return 0; + } + } + return -1; +} + +static int8_t ns_dyn_mem_tracker_lib_find_caller_index(ns_dyn_mem_tracker_lib_conf_t *conf, void *caller_addr, uint16_t *caller_index) +{ + for (uint16_t index = 0; index <= conf->last_mem_block_index; index++) { + if (conf->mem_blocks[index].caller_addr == caller_addr) { + *caller_index = index; + return 0; + } + } + return -1; +} + +static int8_t ns_dyn_mem_tracker_lib_find_block_index(ns_dyn_mem_tracker_lib_conf_t *conf, void *block, uint16_t *block_index) +{ + for (uint16_t index = 0; index <= conf->last_mem_block_index; index++) { + if (conf->mem_blocks[index].block == block) { + *block_index = index; + return 0; + } + } + return -1; +} + +static int8_t ns_dyn_mem_tracker_lib_ext_find_free_index(ns_dyn_mem_tracker_lib_conf_t *conf, uint32_t start_index, uint32_t *free_index) +{ + for (uint32_t index = start_index; index < conf->ext_mem_blocks_count; index++) { + if (conf->ext_mem_blocks[index].caller_addr == NULL) { + *free_index = index; + return 0; + } + } + + if (start_index == 0) { + return -1; + } + + for (uint32_t index = 0; index < start_index; index++) { + if (conf->ext_mem_blocks[index].caller_addr == NULL) { + *free_index = index; + return 0; + } + } + + return -1; +} + +static int8_t ns_dyn_mem_tracker_lib_ext_find_block_index(ns_dyn_mem_tracker_lib_conf_t *conf, void *block, uint32_t start_index, uint32_t *block_index) +{ + for (uint32_t index = start_index; index < conf->ext_mem_blocks_count; index++) { + if (conf->ext_mem_blocks[index].block == block) { + *block_index = index; + return 0; + } + } + + if (start_index == 0) { + return -1; + } + + for (uint32_t index = 0; index < start_index; index++) { + if (conf->ext_mem_blocks[index].block == block) { + *block_index = index; + return 0; + } + } + + return -1; +} + +#endif diff --git a/connectivity/libraries/nanostack-libservice/test/libService/unittest/nsdynmem/dynmemtest.cpp b/connectivity/libraries/nanostack-libservice/test/libService/unittest/nsdynmem/dynmemtest.cpp index 1face7ca2a..8967d3cd2f 100644 --- a/connectivity/libraries/nanostack-libservice/test/libService/unittest/nsdynmem/dynmemtest.cpp +++ b/connectivity/libraries/nanostack-libservice/test/libService/unittest/nsdynmem/dynmemtest.cpp @@ -400,12 +400,12 @@ TEST(dynmem, ns_dyn_mem_temporary_alloc_with_heap_threshold) CHECK(info.heap_sector_alloc_cnt == 0); free(heap); - // Test6, feature is disabled if info is not set + // Test6, feature is disabled if info is coming anyway heap = (uint8_t *)malloc(size); CHECK(NULL != heap); ns_dyn_mem_init(heap, size, &heap_fail_callback, NULL); ret_val = ns_dyn_mem_set_temporary_alloc_free_heap_threshold(0, 0); - CHECK(ret_val == -1); + CHECK(ret_val == 0); CHECK(!heap_have_failed()); free(heap); }