diff --git a/features/frameworks/nanostack-libservice/Makefile b/features/frameworks/nanostack-libservice/Makefile index 24fd3418e9..390ff1f777 100644 --- a/features/frameworks/nanostack-libservice/Makefile +++ b/features/frameworks/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/features/frameworks/nanostack-libservice/mbed-client-libservice/nsdynmemLIB.h b/features/frameworks/nanostack-libservice/mbed-client-libservice/nsdynmemLIB.h index bf0056ba1a..4765e3ef15 100644 --- a/features/frameworks/nanostack-libservice/mbed-client-libservice/nsdynmemLIB.h +++ b/features/frameworks/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. @@ -99,7 +102,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 +116,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 +130,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/features/frameworks/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker.h b/features/frameworks/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker.h new file mode 100644 index 0000000000..c0fccdf97e --- /dev/null +++ b/features/frameworks/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/features/frameworks/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker_lib.h b/features/frameworks/nanostack-libservice/mbed-client-libservice/nsdynmem_tracker_lib.h new file mode 100644 index 0000000000..aa76f390bf --- /dev/null +++ b/features/frameworks/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/features/frameworks/nanostack-libservice/mbed_lib.json b/features/frameworks/nanostack-libservice/mbed_lib.json index 75957496a9..3814598740 100644 --- a/features/frameworks/nanostack-libservice/mbed_lib.json +++ b/features/frameworks/nanostack-libservice/mbed_lib.json @@ -1,3 +1,10 @@ { - "name": "nanostack-libservice" + "name": "nanostack-libservice", + "macros": ["NSDYNMEM_TRACKER_ENABLED=MBED_CONF_NANOSTACK_LIBSERVICE_NSDYNMEM_TRACKER_ENABLED"], + "config": { + "nsdynmem-tracker-enabled": { + "help": "Use to enable dynamic memory tracker", + "value": 0 + } + } } diff --git a/features/frameworks/nanostack-libservice/source/nsdynmemLIB/nsdynmemLIB.c b/features/frameworks/nanostack-libservice/source/nsdynmemLIB/nsdynmemLIB.c index 5f913d5758..f6e1f8412b 100644 --- a/features/frameworks/nanostack-libservice/source/nsdynmemLIB/nsdynmemLIB.c +++ b/features/frameworks/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 diff --git a/features/frameworks/nanostack-libservice/source/nsdynmemtracker/nsdynmem_tracker_lib.c b/features/frameworks/nanostack-libservice/source/nsdynmemtracker/nsdynmem_tracker_lib.c new file mode 100644 index 0000000000..9c2bcdfda4 --- /dev/null +++ b/features/frameworks/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