diff --git a/Makefile b/Makefile index 4f527b674..4ba14a166 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ MCU_CFLAGS += -ffunction-sections -fdata-sections DEBUG_OPTIONS = -g3 -O0 -DDEBUG #-fsanitize=address RELEASE_OPTIONS = -Os -Werror -DNDEBUG -DEFINES = -DMEM_HEAP_CHUNK_SIZE=256 -DMEM_HEAP_AREA_SIZE=32768 +DEFINES = -DMEM_HEAP_CHUNK_SIZE=256 -DMEM_HEAP_AREA_SIZE=32768 -DMEM_STATS TARGET_HOST = -D__HOST TARGET_MCU = -D__MCU diff --git a/src/liballocator/mem-heap.c b/src/liballocator/mem-heap.c index 252307e0c..e42b6dd61 100644 --- a/src/liballocator/mem-heap.c +++ b/src/liballocator/mem-heap.c @@ -64,22 +64,12 @@ typedef enum */ typedef struct mem_BlockHeader_t { - mem_MagicNumOfBlock_t m_MagicNum; /**< magic number - MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK for allocated block - and MEM_MAGIC_NUM_OF_FREE_BLOCK for free block */ + mem_MagicNumOfBlock_t m_MagicNum; /**< magic number - MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK for allocated block + and MEM_MAGIC_NUM_OF_FREE_BLOCK for free block */ struct mem_BlockHeader_t *m_Neighbours[ MEM_DIRECTION_COUNT ]; /**< neighbour blocks */ - size_t m_SizeInChunks; /**< size of block with header in chunks */ + size_t allocated_bytes; /**< allocated area size - for allocated blocks; 0 - for free blocks */ } mem_BlockHeader_t; -/** - * Calculate size in bytes of the block space, that can be used to store data - */ -#define mem_GetHeapBlockDataSpaceSizeInBytes( pBlockHeader) ( MEM_HEAP_CHUNK_SIZE * pBlockHeader->m_SizeInChunks - sizeof(mem_BlockHeader_t) ) - -/** - * Calculate size in chunks of heap block from data space size in bytes - */ -#define mem_GetHeapBlockSizeInChunksFromDataSpaceSizeInBytes( size) ( ( sizeof(mem_BlockHeader_t) + size + MEM_HEAP_CHUNK_SIZE - 1 ) / MEM_HEAP_CHUNK_SIZE ) - /** * Chunk should have enough space for block header */ @@ -95,8 +85,8 @@ JERRY_STATIC_ASSERT( MEM_HEAP_CHUNK_SIZE % MEM_ALIGNMENT == 0 ); */ typedef struct { - uint8_t* m_HeapStart; /**< first address of heap space */ - size_t m_HeapSize; /**< heap space size */ + uint8_t* m_HeapStart; /**< first address of heap space */ + size_t m_HeapSize; /**< heap space size */ mem_BlockHeader_t* m_pFirstBlock; /**< first block of the heap */ mem_BlockHeader_t* m_pLastBlock; /**< last block of the heap */ } mem_HeapState_t; @@ -106,6 +96,10 @@ typedef struct */ mem_HeapState_t mem_Heap; +static inline size_t mem_get_block_chunks_count( const mem_BlockHeader_t *block_header_p); +static inline size_t mem_get_block_data_space_size( const mem_BlockHeader_t *block_header_p); +static inline size_t mem_get_block_chunks_count_from_data_size( size_t block_allocated_size); + static void mem_InitBlockHeader( uint8_t *pFirstChunk, size_t sizeInChunks, mem_BlockState_t blockState, @@ -113,12 +107,80 @@ static void mem_InitBlockHeader( uint8_t *pFirstChunk, mem_BlockHeader_t *pNextBlock); static void mem_CheckHeap( void); +#ifdef MEM_STATS +/** + * Heap's memory usage statistics + */ +static mem_HeapStats_t mem_HeapStats; + +static void mem_HeapStatInit( void); +static void mem_HeapStatAllocBlock( mem_BlockHeader_t *block_header_p); +static void mem_HeapStatFreeBlock( mem_BlockHeader_t *block_header_p); +static void mem_HeapStatFreeBlockSplit( void); +static void mem_HeapStatFreeBlockMerge( void); +#else /* !MEM_STATS */ +# define mem_InitStats() +# define mem_HeapStatAllocBlock( v) +# define mem_HeapStatFreeBlock( v) +# define mem_HeapStatFreeBlockSplit() +# define mem_HeapStatFreeBlockMerge() +#endif /* !MEM_STATS */ + +/** + * get chunk count, used by the block. + * + * @return chunks count + */ +static inline size_t +mem_get_block_chunks_count( const mem_BlockHeader_t *block_header_p) /**< block header */ +{ + JERRY_ASSERT( block_header_p != NULL ); + + const mem_BlockHeader_t *next_block_p = block_header_p->m_Neighbours[ MEM_DIRECTION_NEXT ]; + size_t dist_till_block_end; + + if ( next_block_p == NULL ) + { + dist_till_block_end = (size_t) ( mem_Heap.m_HeapStart + mem_Heap.m_HeapSize - (uint8_t*) block_header_p ); + } else + { + dist_till_block_end = (size_t) ( (uint8_t*) next_block_p - (uint8_t*) block_header_p ); + } + + JERRY_ASSERT( dist_till_block_end <= mem_Heap.m_HeapSize ); + JERRY_ASSERT( dist_till_block_end % MEM_HEAP_CHUNK_SIZE == 0 ); + + return dist_till_block_end / MEM_HEAP_CHUNK_SIZE; +} /* mem_get_block_chunks_count */ + +/** + * Calculate block's data space size + * + * @return size of block area that can be used to store data + */ +static inline size_t +mem_get_block_data_space_size( const mem_BlockHeader_t *block_header_p) /**< block header */ +{ + return mem_get_block_chunks_count( block_header_p) * MEM_HEAP_CHUNK_SIZE - sizeof (mem_BlockHeader_t); +} /* mem_get_block_data_space_size */ + +/** + * Calculate minimum chunks count needed for block with specified size of allocated data area. + * + * @return chunks count + */ +static inline size_t +mem_get_block_chunks_count_from_data_size( size_t block_allocated_size) /**< size of block's allocated area */ +{ + return JERRY_ALIGNUP( sizeof (mem_BlockHeader_t) + block_allocated_size, MEM_HEAP_CHUNK_SIZE) / MEM_HEAP_CHUNK_SIZE; +} /* mem_get_block_chunks_count_from_data_size */ + /** * Startup initialization of heap */ void -mem_HeapInit( uint8_t *heapStart, /**< first address of heap space */ - size_t heapSize) /**< heap space size */ +mem_HeapInit(uint8_t *heapStart, /**< first address of heap space */ + size_t heapSize) /**< heap space size */ { JERRY_ASSERT( heapStart != NULL ); JERRY_ASSERT( heapSize != 0 ); @@ -128,22 +190,24 @@ mem_HeapInit( uint8_t *heapStart, /**< first address of heap space */ mem_Heap.m_HeapStart = heapStart; mem_Heap.m_HeapSize = heapSize; - mem_InitBlockHeader( mem_Heap.m_HeapStart, - heapSize / MEM_HEAP_CHUNK_SIZE, + mem_InitBlockHeader(mem_Heap.m_HeapStart, + 0, MEM_BLOCK_FREE, NULL, NULL); mem_Heap.m_pFirstBlock = (mem_BlockHeader_t*) mem_Heap.m_HeapStart; mem_Heap.m_pLastBlock = mem_Heap.m_pFirstBlock; + + mem_HeapStatInit(); } /* mem_HeapInit */ /** * Initialize block header */ static void -mem_InitBlockHeader( uint8_t *pFirstChunk, /**< address of the first chunk to use for the block */ - size_t sizeInChunks, /**< size of block with header in chunks */ +mem_InitBlockHeader( uint8_t *pFirstChunk, /**< address of the first chunk to use for the block */ + size_t allocated_bytes, /**< size of block's allocated area */ mem_BlockState_t blockState, /**< state of the block (allocated or free) */ mem_BlockHeader_t *pPrevBlock, /**< previous block */ mem_BlockHeader_t *pNextBlock) /**< next block */ @@ -151,17 +215,21 @@ mem_InitBlockHeader( uint8_t *pFirstChunk, /**< address of the fir mem_BlockHeader_t *pBlockHeader = (mem_BlockHeader_t*) pFirstChunk; if ( blockState == MEM_BLOCK_FREE ) - { - pBlockHeader->m_MagicNum = MEM_MAGIC_NUM_OF_FREE_BLOCK; - } else - { - pBlockHeader->m_MagicNum = MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK; - } + { + pBlockHeader->m_MagicNum = MEM_MAGIC_NUM_OF_FREE_BLOCK; - pBlockHeader->m_Neighbours[ MEM_DIRECTION_PREV ] = pPrevBlock; - pBlockHeader->m_Neighbours[ MEM_DIRECTION_NEXT ] = pNextBlock; - pBlockHeader->m_SizeInChunks = sizeInChunks; -} /* mem_InitFreeBlock */ + JERRY_ASSERT( allocated_bytes == 0 ); + } else + { + pBlockHeader->m_MagicNum = MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK; + } + + pBlockHeader->m_Neighbours[ MEM_DIRECTION_PREV ] = pPrevBlock; + pBlockHeader->m_Neighbours[ MEM_DIRECTION_NEXT ] = pNextBlock; + pBlockHeader->allocated_bytes = allocated_bytes; + + JERRY_ASSERT( allocated_bytes <= mem_get_block_data_space_size( pBlockHeader) ); +} /* mem_InitBlockHeader */ /** * Allocation of memory region. @@ -177,7 +245,7 @@ mem_InitBlockHeader( uint8_t *pFirstChunk, /**< address of the fir * NULL - if there is not enough memory. */ uint8_t* -mem_HeapAllocBlock( size_t sizeInBytes, /**< size of region to allocate in bytes */ +mem_HeapAllocBlock( size_t sizeInBytes, /**< size of region to allocate in bytes */ mem_HeapAllocTerm_t allocTerm) /**< expected allocation term */ { mem_BlockHeader_t *pBlock; @@ -186,82 +254,86 @@ mem_HeapAllocBlock( size_t sizeInBytes, /**< size of region to allo mem_CheckHeap(); if ( allocTerm == MEM_HEAP_ALLOC_SHORT_TERM ) + { + pBlock = mem_Heap.m_pFirstBlock; + direction = MEM_DIRECTION_NEXT; + } else + { + pBlock = mem_Heap.m_pLastBlock; + direction = MEM_DIRECTION_PREV; + } + + /* searching for appropriate block */ + while ( pBlock != NULL ) + { + if ( pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK ) { - pBlock = mem_Heap.m_pFirstBlock; - direction = MEM_DIRECTION_NEXT; + if ( mem_get_block_data_space_size( pBlock) >= sizeInBytes ) + { + break; + } } else - { - pBlock = mem_Heap.m_pLastBlock; - direction = MEM_DIRECTION_PREV; - } + { + JERRY_ASSERT( pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK ); + } - /* searching for appropriate block */ - while ( pBlock != NULL ) - { - if ( pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK ) - { - if ( mem_GetHeapBlockDataSpaceSizeInBytes( pBlock) >= sizeInBytes ) - { - break; - } - } else - { - JERRY_ASSERT( pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK ); - } + pBlock = pBlock->m_Neighbours[ direction ]; + } - pBlock = pBlock->m_Neighbours[ direction ]; - } + if ( pBlock == NULL ) + { + /* not enough free space */ + return NULL; + } - if ( pBlock == NULL ) - { - /* not enough free space */ - return NULL; - } + /* appropriate block found, allocating space */ + size_t newBlockSizeInChunks = mem_get_block_chunks_count_from_data_size( sizeInBytes); + size_t foundBlockSizeInChunks = mem_get_block_chunks_count( pBlock); - /* appropriate block found, allocating space */ - size_t newBlockSizeInChunks = mem_GetHeapBlockSizeInChunksFromDataSpaceSizeInBytes( sizeInBytes); - size_t foundBlockSizeInChunks = pBlock->m_SizeInChunks; + JERRY_ASSERT( newBlockSizeInChunks <= foundBlockSizeInChunks ); - JERRY_ASSERT( newBlockSizeInChunks <= foundBlockSizeInChunks ); + mem_BlockHeader_t *pPrevBlock = pBlock->m_Neighbours[ MEM_DIRECTION_PREV ]; + mem_BlockHeader_t *pNextBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]; - mem_BlockHeader_t *pPrevBlock = pBlock->m_Neighbours[ MEM_DIRECTION_PREV ]; - mem_BlockHeader_t *pNextBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]; + if ( newBlockSizeInChunks < foundBlockSizeInChunks ) + { + mem_HeapStatFreeBlockSplit(); - if ( newBlockSizeInChunks < foundBlockSizeInChunks ) - { - uint8_t *pNewFreeBlockFirstChunk = (uint8_t*) pBlock + newBlockSizeInChunks * MEM_HEAP_CHUNK_SIZE; - mem_InitBlockHeader( pNewFreeBlockFirstChunk, - foundBlockSizeInChunks - newBlockSizeInChunks, - MEM_BLOCK_FREE, - pBlock /* there we will place new allocated block */, - pNextBlock); - - mem_BlockHeader_t *pNewFreeBlock = (mem_BlockHeader_t*) pNewFreeBlockFirstChunk; - - if ( pNextBlock == NULL ) - { - mem_Heap.m_pLastBlock = pNewFreeBlock; - } - - pNextBlock = pNewFreeBlock; - } - - mem_InitBlockHeader( (uint8_t*) pBlock, - newBlockSizeInChunks, - MEM_BLOCK_ALLOCATED, - pPrevBlock, + uint8_t *pNewFreeBlockFirstChunk = (uint8_t*) pBlock + newBlockSizeInChunks * MEM_HEAP_CHUNK_SIZE; + mem_InitBlockHeader(pNewFreeBlockFirstChunk, + 0, + MEM_BLOCK_FREE, + pBlock /* there we will place new allocated block */, pNextBlock); - JERRY_ASSERT( mem_GetHeapBlockDataSpaceSizeInBytes( pBlock) >= sizeInBytes ); + mem_BlockHeader_t *pNewFreeBlock = (mem_BlockHeader_t*) pNewFreeBlockFirstChunk; - mem_CheckHeap(); + if ( pNextBlock == NULL ) + { + mem_Heap.m_pLastBlock = pNewFreeBlock; + } - /* return data space beginning address */ - uint8_t *pDataSpace = (uint8_t*) (pBlock + 1); - JERRY_ASSERT( (uintptr_t) pDataSpace % MEM_ALIGNMENT == 0); + pNextBlock = pNewFreeBlock; + } - return pDataSpace; -} /* mem_Alloc */ + mem_InitBlockHeader((uint8_t*) pBlock, + sizeInBytes, + MEM_BLOCK_ALLOCATED, + pPrevBlock, + pNextBlock); + + mem_HeapStatAllocBlock( pBlock); + + JERRY_ASSERT( mem_get_block_data_space_size( pBlock) >= sizeInBytes ); + + mem_CheckHeap(); + + /* return data space beginning address */ + uint8_t *pDataSpace = (uint8_t*) (pBlock + 1); + JERRY_ASSERT( (uintptr_t) pDataSpace % MEM_ALIGNMENT == 0); + + return pDataSpace; +} /* mem_HeapAllocBlock */ /** * Free the memory block. @@ -279,49 +351,53 @@ mem_HeapFreeBlock( uint8_t *ptr) /**< pointer to beginning of data space of the mem_BlockHeader_t *pPrevBlock = pBlock->m_Neighbours[ MEM_DIRECTION_PREV ]; mem_BlockHeader_t *pNextBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]; + mem_HeapStatFreeBlock( pBlock); + /* checking magic nums that are neighbour to data space */ JERRY_ASSERT( pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK ); if ( pNextBlock != NULL ) - { - JERRY_ASSERT( pNextBlock->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK - || pNextBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK ); - } + { + JERRY_ASSERT( pNextBlock->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK + || pNextBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK ); + } pBlock->m_MagicNum = MEM_MAGIC_NUM_OF_FREE_BLOCK; if ( pNextBlock != NULL - && pNextBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK ) + && pNextBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK ) + { + /* merge with the next block */ + mem_HeapStatFreeBlockMerge(); + + pNextBlock = pNextBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]; + pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ] = pNextBlock; + if ( pNextBlock != NULL ) { - /* merge with the next block */ - pBlock->m_SizeInChunks += pNextBlock->m_SizeInChunks; - pNextBlock = pNextBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]; - pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ] = pNextBlock; - if ( pNextBlock != NULL ) - { - pNextBlock->m_Neighbours[ MEM_DIRECTION_PREV ] = pBlock; - } else - { - mem_Heap.m_pLastBlock = pBlock; - } + pNextBlock->m_Neighbours[ MEM_DIRECTION_PREV ] = pBlock; + } else + { + mem_Heap.m_pLastBlock = pBlock; } + } if ( pPrevBlock != NULL - && pPrevBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK ) + && pPrevBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK ) + { + /* merge with the previous block */ + mem_HeapStatFreeBlockMerge(); + + pPrevBlock->m_Neighbours[ MEM_DIRECTION_NEXT ] = pNextBlock; + if ( pNextBlock != NULL ) { - /* merge with the previous block */ - pPrevBlock->m_SizeInChunks += pBlock->m_SizeInChunks; - pPrevBlock->m_Neighbours[ MEM_DIRECTION_NEXT ] = pNextBlock; - if ( pNextBlock != NULL ) - { - pNextBlock->m_Neighbours[ MEM_DIRECTION_PREV ] = pBlock->m_Neighbours[ MEM_DIRECTION_PREV ]; - } else - { - mem_Heap.m_pLastBlock = pPrevBlock; - } + pNextBlock->m_Neighbours[ MEM_DIRECTION_PREV ] = pBlock->m_Neighbours[ MEM_DIRECTION_PREV ]; + } else + { + mem_Heap.m_pLastBlock = pPrevBlock; } + } mem_CheckHeap(); -} /* mem_Free */ +} /* mem_HeapFreeBlock */ /** * Recommend allocation size based on chunk size. @@ -353,28 +429,52 @@ mem_HeapPrint( bool dumpBlockData) /**< print block with data (true) (void*) mem_Heap.m_pLastBlock); for ( mem_BlockHeader_t *pBlock = mem_Heap.m_pFirstBlock; - pBlock != NULL; - pBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ] ) - { - libc_printf("Block (%p): magic num=0x%08x, size in chunks=%lu, previous block->%p next block->%p\n", - (void*) pBlock, - pBlock->m_MagicNum, - pBlock->m_SizeInChunks, - (void*) pBlock->m_Neighbours[ MEM_DIRECTION_PREV ], - (void*) pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]); + pBlock != NULL; + pBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ] ) + { + libc_printf("Block (%p): magic num=0x%08x, size in chunks=%lu, previous block->%p next block->%p\n", + (void*) pBlock, + pBlock->m_MagicNum, + mem_get_block_chunks_count( pBlock), + (void*) pBlock->m_Neighbours[ MEM_DIRECTION_PREV ], + (void*) pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]); - if ( dumpBlockData ) - { - uint8_t *pBlockData = (uint8_t*) (pBlock + 1); - for ( uint32_t offset = 0; - offset < mem_GetHeapBlockDataSpaceSizeInBytes( pBlock); - offset++ ) - { - libc_printf("%02x ", pBlockData[ offset ]); - } - libc_printf("\n"); - } + if ( dumpBlockData ) + { + uint8_t *pBlockData = (uint8_t*) (pBlock + 1); + for ( uint32_t offset = 0; + offset < mem_get_block_data_space_size( pBlock); + offset++ ) + { + libc_printf("%02x ", pBlockData[ offset ]); + } + libc_printf("\n"); } + } + +#ifdef MEM_STATS + libc_printf("Heap stats:\n"); + libc_printf(" Size = %lu\n" + " Blocks count = %lu\n" + " Allocated blocks count = %lu\n" + " Allocated chunks count = %lu\n" + " Allocated bytes count = %lu\n" + " Waste bytes count = %lu\n" + " Peak allocated blocks count = %lu\n" + " Peak allocated chunks count = %lu\n" + " Peak allocated bytes count = %lu\n" + " Peak waste bytes count = %lu\n", + mem_HeapStats.size, + mem_HeapStats.blocks, + mem_HeapStats.allocated_blocks, + mem_HeapStats.allocated_chunks, + mem_HeapStats.allocated_bytes, + mem_HeapStats.waste_bytes, + mem_HeapStats.peak_allocated_blocks, + mem_HeapStats.peak_allocated_chunks, + mem_HeapStats.peak_allocated_bytes, + mem_HeapStats.peak_waste_bytes); +#endif /* MEM_STATS */ libc_printf("\n"); } /* mem_PrintHeap */ @@ -389,38 +489,141 @@ mem_CheckHeap( void) JERRY_ASSERT( (uint8_t*) mem_Heap.m_pFirstBlock == mem_Heap.m_HeapStart ); JERRY_ASSERT( mem_Heap.m_HeapSize % MEM_HEAP_CHUNK_SIZE == 0 ); - size_t chunksCount = 0; bool isLastBlockWasMet = false; for ( mem_BlockHeader_t *pBlock = mem_Heap.m_pFirstBlock; - pBlock != NULL; - pBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ] ) + pBlock != NULL; + pBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ] ) + { + JERRY_ASSERT( pBlock != NULL ); + JERRY_ASSERT( pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK + || pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK ); + + mem_BlockHeader_t *pNextBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]; + if ( pBlock == mem_Heap.m_pLastBlock ) { - JERRY_ASSERT( pBlock != NULL ); - JERRY_ASSERT( pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_FREE_BLOCK - || pBlock->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK ); + isLastBlockWasMet = true; - chunksCount += pBlock->m_SizeInChunks; - - mem_BlockHeader_t *pNextBlock = pBlock->m_Neighbours[ MEM_DIRECTION_NEXT ]; - if ( pBlock == mem_Heap.m_pLastBlock ) - { - isLastBlockWasMet = true; - - JERRY_ASSERT( pNextBlock == NULL ); - JERRY_ASSERT( mem_Heap.m_HeapStart + mem_Heap.m_HeapSize - == (uint8_t*) pBlock + pBlock->m_SizeInChunks * MEM_HEAP_CHUNK_SIZE ); - } else - { - JERRY_ASSERT( pNextBlock != NULL ); - JERRY_ASSERT( (uint8_t*) pNextBlock == (uint8_t*) pBlock + pBlock->m_SizeInChunks * MEM_HEAP_CHUNK_SIZE ); - } + JERRY_ASSERT( pNextBlock == NULL ); + } else + { + JERRY_ASSERT( pNextBlock != NULL ); } + } JERRY_ASSERT( isLastBlockWasMet ); - JERRY_ASSERT( chunksCount == mem_Heap.m_HeapSize / MEM_HEAP_CHUNK_SIZE ); #endif /* !JERRY_NDEBUG */ } /* mem_CheckHeap */ +#ifdef MEM_STATS +/** + * Get heap memory usage statistics + */ +void +mem_HeapGetStats( mem_HeapStats_t *out_heap_stats_p) /**< out: heap stats */ +{ + *out_heap_stats_p = mem_HeapStats; +} /* mem_HeapGetStats */ + +/** + * Initalize heap memory usage statistics account structure + */ +static void +mem_HeapStatInit() +{ + libc_memset( &mem_HeapStats, 0, sizeof (mem_HeapStats)); + + mem_HeapStats.size = mem_Heap.m_HeapSize; + mem_HeapStats.blocks = 1; +} /* mem_InitStats */ + +/** + * Account block allocation + */ +static void +mem_HeapStatAllocBlock( mem_BlockHeader_t *block_header_p) /**< allocated block */ +{ + JERRY_ASSERT( block_header_p->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK ); + + const size_t chunks = mem_get_block_chunks_count( block_header_p); + const size_t bytes = block_header_p->allocated_bytes; + const size_t waste_bytes = chunks * MEM_HEAP_CHUNK_SIZE - bytes; + + mem_HeapStats.allocated_blocks++; + mem_HeapStats.allocated_chunks += chunks; + mem_HeapStats.allocated_bytes += bytes; + mem_HeapStats.waste_bytes += waste_bytes; + + if ( mem_HeapStats.allocated_blocks > mem_HeapStats.peak_allocated_blocks ) + { + mem_HeapStats.peak_allocated_blocks = mem_HeapStats.allocated_blocks; + } + + if ( mem_HeapStats.allocated_chunks > mem_HeapStats.peak_allocated_chunks ) + { + mem_HeapStats.peak_allocated_chunks = mem_HeapStats.allocated_chunks; + } + + if ( mem_HeapStats.allocated_bytes > mem_HeapStats.peak_allocated_bytes ) + { + mem_HeapStats.peak_allocated_bytes = mem_HeapStats.allocated_bytes; + } + + if ( mem_HeapStats.waste_bytes > mem_HeapStats.peak_waste_bytes ) + { + mem_HeapStats.peak_waste_bytes = mem_HeapStats.waste_bytes; + } + + JERRY_ASSERT( mem_HeapStats.allocated_blocks <= mem_HeapStats.blocks ); + JERRY_ASSERT( mem_HeapStats.allocated_bytes <= mem_HeapStats.size ); + JERRY_ASSERT( mem_HeapStats.allocated_chunks <= mem_HeapStats.size / MEM_HEAP_CHUNK_SIZE ); +} /* mem_HeapStatAllocBlock */ + +/** + * Account block freeing + */ +static void +mem_HeapStatFreeBlock( mem_BlockHeader_t *block_header_p) /**< block to be freed */ +{ + JERRY_ASSERT( block_header_p->m_MagicNum == MEM_MAGIC_NUM_OF_ALLOCATED_BLOCK ); + + const size_t chunks = mem_get_block_chunks_count( block_header_p); + const size_t bytes = block_header_p->allocated_bytes; + const size_t waste_bytes = chunks * MEM_HEAP_CHUNK_SIZE - bytes; + + JERRY_ASSERT( mem_HeapStats.allocated_blocks <= mem_HeapStats.blocks ); + JERRY_ASSERT( mem_HeapStats.allocated_bytes <= mem_HeapStats.size ); + JERRY_ASSERT( mem_HeapStats.allocated_chunks <= mem_HeapStats.size / MEM_HEAP_CHUNK_SIZE ); + + JERRY_ASSERT( mem_HeapStats.allocated_blocks >= 1 ); + JERRY_ASSERT( mem_HeapStats.allocated_chunks >= chunks ); + JERRY_ASSERT( mem_HeapStats.allocated_bytes >= bytes ); + JERRY_ASSERT( mem_HeapStats.waste_bytes >= waste_bytes ); + + mem_HeapStats.allocated_blocks--; + mem_HeapStats.allocated_chunks -= chunks; + mem_HeapStats.allocated_bytes -= bytes; + mem_HeapStats.waste_bytes -= waste_bytes; +} /* mem_HeapStatFreeBlock */ + +/** + * Account free block split + */ +static void +mem_HeapStatFreeBlockSplit( void) +{ + mem_HeapStats.blocks++; +} /* mem_HeapStatFreeBlockSplit */ + +/** + * Account free block merge + */ +static void +mem_HeapStatFreeBlockMerge( void) +{ + mem_HeapStats.blocks--; +} /* mem_HeapStatFreeBlockMerge */ +#endif /* MEM_STATS */ + /** * @} * @} diff --git a/src/liballocator/mem-heap.h b/src/liballocator/mem-heap.h index 4718c0986..0c8ee39bb 100644 --- a/src/liballocator/mem-heap.h +++ b/src/liballocator/mem-heap.h @@ -44,6 +44,31 @@ extern void mem_HeapFreeBlock(uint8_t *ptr); extern size_t mem_HeapRecommendAllocationSize(size_t minimumAllocationSize); extern void mem_HeapPrint(bool dumpBlockData); +#ifdef MEM_STATS +/** + * Heap memory usage statistics + */ +typedef struct { + size_t size; /**< size */ + size_t blocks; /**< blocks count */ + + size_t allocated_chunks; /**< currently allocated chunks */ + size_t peak_allocated_chunks; /**< peak allocated chunks */ + + size_t allocated_blocks; /**< currently allocated blocks */ + size_t peak_allocated_blocks; /**< peak allocated blocks */ + + size_t allocated_bytes; /**< currently allocated bytes */ + size_t peak_allocated_bytes; /**< peak allocated bytes */ + + size_t waste_bytes; /**< bytes waste due to blocks filled partially + and due to block headers */ + size_t peak_waste_bytes; /**< peak bytes waste */ +} mem_HeapStats_t; + +extern void mem_HeapGetStats(mem_HeapStats_t *out_heap_stats_p); +#endif /* MEM_STATS */ + /** * @} * @} diff --git a/src/liballocator/mem-poolman.c b/src/liballocator/mem-poolman.c index 6e2b4fe1e..2477391db 100644 --- a/src/liballocator/mem-poolman.c +++ b/src/liballocator/mem-poolman.c @@ -27,6 +27,7 @@ #define JERRY_MEM_POOL_INTERNAL #include "globals.h" +#include "jerry-libc.h" #include "mem-allocator.h" #include "mem-heap.h" #include "mem-pool.h" @@ -52,6 +53,25 @@ mem_PoolState_t mem_PoolForPoolHeaders; */ uint8_t *mem_SpaceForPoolForPoolHeaders; +#ifdef MEM_STATS +/** +* Pools' memory usage statistics + */ +mem_PoolsStats_t mem_PoolsStats; + +static void mem_PoolsStatInit( void); +static void mem_PoolsStatAllocPool( mem_PoolChunkType_t); +static void mem_PoolsStatFreePool( mem_PoolChunkType_t); +static void mem_PoolsStatAllocChunk( mem_PoolChunkType_t); +static void mem_PoolsStatFreeChunk( mem_PoolChunkType_t); +#else /* !MEM_STATS */ +# define mem_PoolsStatsInit() +# define mem_PoolsStatAllocPool() +# define mem_PoolsStatsFreePool() +# define mem_PoolsStatAllocChunk() +# define mem_PoolsStatFreeChunk() +#endif /* !MEM_STATS */ + /** * Initialize pool manager */ @@ -83,6 +103,8 @@ mem_PoolsInit(void) mem_GetChunkSize( chunkType), mem_SpaceForPoolForPoolHeaders, poolSpaceSize); + + mem_PoolsStatInit(); } /* mem_PoolsInit */ /** @@ -138,6 +160,8 @@ mem_PoolsAlloc( mem_PoolChunkType_t chunkType) /**< chunk type */ mem_Pools[ chunkType ] = poolState; mem_FreeChunksNumber[ chunkType ] += poolState->m_FreeChunksNumber; + + mem_PoolsStatAllocPool( chunkType); } /** @@ -159,6 +183,8 @@ mem_PoolsAlloc( mem_PoolChunkType_t chunkType) /**< chunk type */ */ mem_FreeChunksNumber[ chunkType ]--; + mem_PoolsStatAllocChunk( chunkType); + return mem_PoolAllocChunk( poolState); } /* mem_PoolsAlloc */ @@ -189,6 +215,8 @@ mem_PoolsFree( mem_PoolChunkType_t chunkType, /**< the chunk type */ mem_PoolFreeChunk( poolState, pChunk); mem_FreeChunksNumber[ chunkType ]++; + mem_PoolsStatFreeChunk( chunkType); + /** * If all chunks of the pool are free, free the pool itself. */ @@ -207,9 +235,89 @@ mem_PoolsFree( mem_PoolChunkType_t chunkType, /**< the chunk type */ mem_HeapFreeBlock( poolState->m_pPoolStart); mem_PoolFreeChunk( &mem_PoolForPoolHeaders, (uint8_t*) poolState); - } + + mem_PoolsStatFreePool( chunkType); + } } /* mem_PoolsFree */ +#ifdef MEM_STATS +/** + * Get pools memory usage statistics + */ +void +mem_PoolsGetStats( mem_PoolsStats_t *out_pools_stats_p) /**< out: pools' stats */ +{ + JERRY_ASSERT( out_pools_stats_p != NULL ); + + *out_pools_stats_p = mem_PoolsStats; +} /* mem_PoolsGetStats */ + +/** + * Initalize pools' memory usage statistics account structure + */ +static void +mem_PoolsStatInit( void) +{ + libc_memset( &mem_PoolsStats, 0, sizeof (mem_PoolsStats)); +} /* mem_PoolsStatInit */ + +/** + * Account allocation of a pool + */ +static void +mem_PoolsStatAllocPool( mem_PoolChunkType_t chunkType) /**< chunk type */ +{ + mem_PoolsStats.pools_count[ chunkType ]++; + mem_PoolsStats.free_chunks[ chunkType ] = mem_FreeChunksNumber[ chunkType ]; + + if ( mem_PoolsStats.pools_count[ chunkType ] > mem_PoolsStats.peak_pools_count[ chunkType ] ) + { + mem_PoolsStats.peak_pools_count[ chunkType ] = mem_PoolsStats.pools_count[ chunkType ]; + } +} /* mem_PoolsStatAllocPool */ + +/** + * Account freeing of a pool + */ +static void +mem_PoolsStatFreePool( mem_PoolChunkType_t chunkType) /**< chunk type */ +{ + JERRY_ASSERT( mem_PoolsStats.pools_count[ chunkType ] > 0 ); + + mem_PoolsStats.pools_count[ chunkType ]--; + mem_PoolsStats.free_chunks[ chunkType ] = mem_FreeChunksNumber[ chunkType ]; +} /* mem_PoolsStatFreePool */ + +/** + * Account allocation of chunk in a pool + */ +static void +mem_PoolsStatAllocChunk( mem_PoolChunkType_t chunkType) /**< chunk type */ +{ + JERRY_ASSERT( mem_PoolsStats.free_chunks[ chunkType ] > 0 ); + + mem_PoolsStats.allocated_chunks[ chunkType ]++; + mem_PoolsStats.free_chunks[ chunkType ]--; + + if ( mem_PoolsStats.allocated_chunks[ chunkType ] > mem_PoolsStats.peak_allocated_chunks[ chunkType ] ) + { + mem_PoolsStats.peak_allocated_chunks[ chunkType ] = mem_PoolsStats.allocated_chunks[ chunkType ]; + } +} /* mem_PoolsStatAllocChunk */ + +/** + * Account freeing of chunk in a pool + */ +static void +mem_PoolsStatFreeChunk( mem_PoolChunkType_t chunkType) /**< chunk type */ +{ + JERRY_ASSERT( mem_PoolsStats.allocated_chunks[ chunkType ] > 0 ); + + mem_PoolsStats.allocated_chunks[ chunkType ]--; + mem_PoolsStats.free_chunks[ chunkType ]++; +} /* mem_PoolsStatFreeChunk */ +#endif /* MEM_STATS */ + /** * @} */ diff --git a/src/liballocator/mem-poolman.h b/src/liballocator/mem-poolman.h index a669f4160..36da10af9 100644 --- a/src/liballocator/mem-poolman.h +++ b/src/liballocator/mem-poolman.h @@ -71,6 +71,31 @@ extern void mem_PoolsInit(void); extern uint8_t* mem_PoolsAlloc(mem_PoolChunkType_t chunkType); extern void mem_PoolsFree(mem_PoolChunkType_t chunkType, uint8_t *pChunk); +#ifdef MEM_STATS +/** + * Pools' memory usage statistics + */ +typedef struct +{ + /** pools' count, per type */ + size_t pools_count[ MEM_POOL_CHUNK_TYPE__COUNT ]; + + /** peak pools' count, per type */ + size_t peak_pools_count[ MEM_POOL_CHUNK_TYPE__COUNT ]; + + /** allocated chunks count, per type */ + size_t allocated_chunks[ MEM_POOL_CHUNK_TYPE__COUNT ]; + + /** peak allocated chunks count, per type */ + size_t peak_allocated_chunks[ MEM_POOL_CHUNK_TYPE__COUNT ]; + + /** free chunks count, per type */ + size_t free_chunks[ MEM_POOL_CHUNK_TYPE__COUNT ]; +} mem_PoolsStats_t; + +extern void mem_PoolsGetStats( mem_PoolsStats_t *out_pools_stats_p); +#endif /* MEM_STATS */ + #endif /* JERRY_MEM_POOLMAN_H */ /** diff --git a/tests/unit/test_heap.c b/tests/unit/test_heap.c index 862df96fb..f8abfa415 100644 --- a/tests/unit/test_heap.c +++ b/tests/unit/test_heap.c @@ -82,5 +82,7 @@ main( int __unused argc, } } + mem_HeapPrint( false); + return 0; } /* main */ diff --git a/tests/unit/test_poolman.c b/tests/unit/test_poolman.c index d254a360d..654bf300a 100644 --- a/tests/unit/test_poolman.c +++ b/tests/unit/test_poolman.c @@ -93,5 +93,28 @@ main( int __unused argc, } } + mem_PoolsStats_t stats; + mem_PoolsGetStats( &stats); + + libc_printf("Pools stats:\n"); + for(mem_PoolChunkType_t type = 0; + type < MEM_POOL_CHUNK_TYPE__COUNT; + type++) + { + libc_printf(" Chunk size: %u\n" + " Pools: %lu\n" + " Allocated chunks: %lu\n" + " Free chunks: %lu\n" + " Peak pools: %lu\n" + " Peak allocated chunks: %lu\n", + mem_GetChunkSize( type), + stats.pools_count[ type ], + stats.allocated_chunks[ type ], + stats.free_chunks[ type ], + stats.peak_pools_count[ type ], + stats.peak_allocated_chunks[ type ]); + } + libc_printf("\n"); + return 0; } /* main */