lib/os: add statistics tracking to mem_blocks

Both the current and maximum number of allocations in a given memory
blocked are tracked (and can be queried) when the
CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS Kconfig option is selected.

Signed-off-by: Peter Mitsis <peter.mitsis@intel.com>
This commit is contained in:
Peter Mitsis 2022-07-05 11:54:55 -04:00 committed by Fabio Baltieri
parent ee190f49c2
commit d061366f54
4 changed files with 159 additions and 8 deletions

View file

@ -23,6 +23,7 @@ extern "C" {
#include <zephyr/kernel.h>
#include <zephyr/math/ilog2.h>
#include <zephyr/sys/bitarray.h>
#include <zephyr/sys/mem_stats.h>
#define MAX_MULTI_ALLOCATORS 8
@ -93,6 +94,14 @@ struct sys_mem_blocks {
/* Bitmap of allocated blocks */
sys_bitarray_t *bitmap;
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
/* Spinlock guarding access to memory block internals */
struct k_spinlock lock;
uint32_t used_blocks;
uint32_t max_used_blocks;
#endif
};
struct sys_multi_mem_blocks {
@ -285,6 +294,34 @@ int sys_mem_blocks_free(sys_mem_blocks_t *mem_block, size_t count,
*/
int sys_mem_blocks_free_contiguous(sys_mem_blocks_t *mem_block, void *block, size_t count);
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
/**
* @brief Get the runtime statistics of a memory block
*
* This function retrieves the runtime stats for the specified memory block
* @a mem_block and copies it into the memory pointed to by @a stats.
*
* @param mem_block Pointer to system memory block
* @param stats Pointer to struct to copy statistics into
*
* @return -EINVAL if NULL pointer was passed, otherwise 0
*/
int sys_mem_blocks_runtime_stats_get(sys_mem_blocks_t *mem_block,
struct sys_memory_stats *stats);
/**
* @brief Reset the maximum memory block usage
*
* This routine resets the maximum memory usage in the specified memory
* block @a mem_block to match that block's current memory usage.
*
* @param mem_block Pointer to system memory block
*
* @return -EINVAL if NULL pointer was passed, otherwise 0
*/
int sys_mem_blocks_runtime_stats_reset_max(sys_mem_blocks_t *mem_block);
#endif
/**
* @brief Initialize multi memory blocks allocator group
*

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2022 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
*
* @brief Memory Statistics
*/
#ifndef ZEPHYR_INCLUDE_SYS_MEM_STATS_H_
#define ZEPHYR_INCLUDE_SYS_MEM_STATS_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
/* A common structure used to report runtime memory usage statistics */
struct sys_memory_stats {
size_t free_bytes;
size_t allocated_bytes;
size_t max_allocated_bytes;
};
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_SYS_MEM_STATS_H_ */

View file

@ -125,4 +125,12 @@ config SYS_MEM_BLOCKS_LISTENER
This allows application to listen for memory blocks allocator
events, such as memory allocation and de-allocation.
config SYS_MEM_BLOCKS_RUNTIME_STATS
bool "Memory blocks runtime statistics"
depends on SYS_MEM_BLOCKS
help
This option enables the tracking and reporting of the memory
blocks statistics related to the current and maximum number
of allocations in a given memory block.
endmenu

View file

@ -18,18 +18,31 @@ static void *alloc_blocks(sys_mem_blocks_t *mem_block, size_t num_blocks)
uint8_t *blk;
void *ret = NULL;
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
k_spinlock_key_t key = k_spin_lock(&mem_block->lock);
#endif
/* Find an unallocated block */
r = sys_bitarray_alloc(mem_block->bitmap, num_blocks, &offset);
if (r != 0) {
goto out;
if (r == 0) {
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
mem_block->used_blocks += (uint32_t)num_blocks;
if (mem_block->max_used_blocks < mem_block->used_blocks) {
mem_block->max_used_blocks = mem_block->used_blocks;
}
k_spin_unlock(&mem_block->lock, key);
#endif
/* Calculate the start address of the newly allocated block */
blk = mem_block->buffer + (offset << mem_block->blk_sz_shift);
ret = blk;
}
/* Calculate the start address of the newly allocated block */
blk = mem_block->buffer + (offset << mem_block->blk_sz_shift);
ret = blk;
out:
return ret;
}
@ -51,8 +64,19 @@ static int free_blocks(sys_mem_blocks_t *mem_block, void *ptr, size_t num_blocks
goto out;
}
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
k_spinlock_key_t key = k_spin_lock(&mem_block->lock);
#endif
ret = sys_bitarray_free(mem_block->bitmap, num_blocks, offset);
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
if (ret == 0) {
mem_block->used_blocks -= (uint32_t) num_blocks;
}
k_spin_unlock(&mem_block->lock, key);
#endif
out:
return ret;
}
@ -163,12 +187,30 @@ int sys_mem_blocks_get(sys_mem_blocks_t *mem_block, void *in_block, size_t count
goto out;
}
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
k_spinlock_key_t key = k_spin_lock(&mem_block->lock);
#endif
ret = sys_bitarray_test_and_set_region(mem_block->bitmap, count, offset, true);
if (ret != 0) {
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
k_spin_unlock(&mem_block->lock, key);
#endif
ret = -ENOMEM;
goto out;
}
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
mem_block->used_blocks += (uint32_t)count;
if (mem_block->max_used_blocks < mem_block->used_blocks) {
mem_block->max_used_blocks = mem_block->used_blocks;
}
k_spin_unlock(&mem_block->lock, key);
#endif
#ifdef CONFIG_SYS_MEM_BLOCKS_LISTENER
heap_listener_notify_alloc(HEAP_ID_FROM_POINTER(mem_block),
in_block, count << mem_block->blk_sz_shift);
@ -354,3 +396,33 @@ int sys_multi_mem_blocks_free(sys_multi_mem_blocks_t *group,
out:
return ret;
}
#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS
int sys_mem_blocks_runtime_stats_get(sys_mem_blocks_t *mem_block,
struct sys_memory_stats *stats)
{
if ((mem_block == NULL) || (stats == NULL)) {
return -EINVAL;
}
stats->allocated_bytes = mem_block->used_blocks <<
mem_block->blk_sz_shift;
stats->free_bytes = (mem_block->num_blocks << mem_block->blk_sz_shift) -
stats->allocated_bytes;
stats->max_allocated_bytes = mem_block->max_used_blocks <<
mem_block->blk_sz_shift;
return 0;
}
int sys_mem_blocks_runtime_stats_reset_max(sys_mem_blocks_t *mem_block)
{
if (mem_block == NULL) {
return -EINVAL;
}
mem_block->max_used_blocks = mem_block->used_blocks;
return 0;
}
#endif