/* * FreeRTOS+TCP V2.2.1 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://aws.amazon.com/freertos * http://www.FreeRTOS.org */ /* * tcp_mem_stats.c * Used to create a CSV file with detaild information about the memory usage of FreeRTOS+TCP. * See tools/tcp_mem_stats.md for further description. */ /* Standard includes. */ #include #include #include /* FreeRTOS includes. */ #include #include "task.h" /* FreeRTOS+TCP includes. */ #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" #include "FreeRTOS_Stream_Buffer.h" #include "FreeRTOS_ARP.h" #include "FreeRTOS_IP_Private.h" #include "tcp_mem_stats.h" #ifndef ipconfigTCP_MEM_STATS_MAX_ALLOCATION #define ipconfigTCP_MEM_STATS_MAX_ALLOCATION 128u #pragma warning "ipconfigTCP_MEM_STATS_MAX_ALLOCATION undefined?" #endif #if( ipconfigUSE_TCP_MEM_STATS != 0 ) /* When a streambuffer is allocated, 4 extra bytes will be reserved. */ #define STREAM_BUFFER_ROUNDUP_BYTES 4 #define STATS_PRINTF( MSG ) \ xCurrentLine++; \ configPRINTF( MSG ) #define ETH_MAX_PACKET_SIZE ( ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 31 ) & ~0x1FuL ) /*-----------------------------------------------------------*/ /* Objects are allocated and deleted. This structure stores the type and the size of the object. */ typedef struct xTCP_ALLOCATION { TCP_MEMORY_t xMemType; void *pxObject; UBaseType_t uxNumber; size_t uxSize; } TCP_ALLOCATION_t; /*-----------------------------------------------------------*/ static void vWriteHeader( void ); static size_t uxCurrentMallocSize; static TCP_ALLOCATION_t xAllocations[ ipconfigTCP_MEM_STATS_MAX_ALLOCATION ]; static size_t uxAllocationCount; static BaseType_t xFirstItem = pdTRUE; UBaseType_t uxNextObjectNumber; static BaseType_t xCurrentLine = 0; static BaseType_t xFirstDumpLine = 0; static BaseType_t xLastHeaderLineNr = 0; static BaseType_t xLoggingStopped = 0; /*-----------------------------------------------------------*/ static void vAddAllocation( TCP_MEMORY_t xMemType, void *pxObject, size_t uxSize ) { size_t uxIndex; vTaskSuspendAll(); { for( uxIndex = 0; uxIndex < uxAllocationCount; uxIndex++ ) { if( xAllocations[ uxIndex ].pxObject == pxObject ) { /* Already added, strange. */ FreeRTOS_printf( ( "vAddAllocation: Pointer %p already added\n", pxObject ) ); return; } } if( uxAllocationCount >= ipconfigTCP_MEM_STATS_MAX_ALLOCATION ) { /* The table is full. */ return; } xAllocations[ uxIndex ].pxObject = pxObject; xAllocations[ uxIndex ].xMemType = xMemType; xAllocations[ uxIndex ].uxSize = uxSize; xAllocations[ uxIndex ].uxNumber = uxNextObjectNumber++; uxAllocationCount++; } xTaskResumeAll(); } /*-----------------------------------------------------------*/ static TCP_ALLOCATION_t *pxRemoveAllocation( void *pxObject ) { size_t uxSource = 0, uxTarget = 0; static TCP_ALLOCATION_t xAllocation = { 0 }; BaseType_t xFound = pdFALSE; TCP_ALLOCATION_t *pxReturn; vTaskSuspendAll(); { for( ; uxSource < uxAllocationCount; uxSource++ ) { if( xAllocations[ uxSource ].pxObject == pxObject ) { /* This is entry will be removed. */ ( void ) memcpy( &( xAllocation ), &( xAllocations[ uxSource ] ), sizeof xAllocation ); xFound = pdTRUE; } else { xAllocations[ uxTarget ] = xAllocations[ uxSource ]; uxTarget++; } } if( xFound != pdFALSE ) { uxAllocationCount--; pxReturn = &( xAllocation ); } else { pxReturn = NULL; } } xTaskResumeAll(); return pxReturn; } /*-----------------------------------------------------------*/ static const char *pcTypeName( TCP_MEMORY_t xMemType ) { switch( xMemType ) { case tcpSOCKET_TCP: return "TCP-Socket"; case tcpSOCKET_UDP: return "UDP-Socket"; case tcpSOCKET_SET: return "SocketSet"; case tcpSEMAPHORE: return "Semaphore"; case tcpRX_STREAM_BUFFER: return "RX-Buffer"; case tcpTX_STREAM_BUFFER: return "TX-Buffer"; case tcpNETWORK_BUFFER: return "networkBuffer"; } return "Unknown object"; } /*-----------------------------------------------------------*/ static void vWriteHeader() { size_t uxPacketSize; size_t uxTXSize; size_t uxStaticSize = 0; BaseType_t xFirstLineNr = 0; char pucComment[ 64 ] = ""; StreamBuffer_t *pxBuffer = NULL; size_t uxTara = sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray ); /* The approximate size of a buffer for a Network Packet. */ STATS_PRINTF( ( "TCPMemStat,Some important ipconfig items:\n" ) ); STATS_PRINTF( ( "TCPMemStat,ipconfig item,Value,Comment\n" ) ); STATS_PRINTF( ( "TCPMemStat,NETWORK_MTU,%u\n", ipconfigNETWORK_MTU ) ); STATS_PRINTF( ( "TCPMemStat,TCP_MSS,%u\n", ipconfigTCP_MSS ) ); STATS_PRINTF( ( "TCPMemStat,USE_TCP,%u\n", ipconfigUSE_TCP ) ); STATS_PRINTF( ( "TCPMemStat,USE_TCP_WIN,%u\n", ipconfigUSE_TCP_WIN ) ); uxTXSize = ( size_t ) FreeRTOS_round_up( ipconfigTCP_TX_BUFFER_LENGTH, ipconfigTCP_MSS ); STATS_PRINTF( ( "TCPMemStat,TCP_RX_BUFFER_LENGTH,%u,Plus %u bytes\n", ipconfigTCP_RX_BUFFER_LENGTH, uxTara + STREAM_BUFFER_ROUNDUP_BYTES ) ); if( uxTXSize > ipconfigTCP_TX_BUFFER_LENGTH ) { snprintf( pucComment, sizeof pucComment, ",Rounded up to %u x MSS (plus %u bytes)", uxTXSize / ipconfigTCP_MSS, uxTara + STREAM_BUFFER_ROUNDUP_BYTES ); } STATS_PRINTF( ( "TCPMemStat,TCP_TX_BUFFER_LENGTH,%u%s\n", ipconfigTCP_TX_BUFFER_LENGTH, pucComment ) ); STATS_PRINTF( ( "TCPMemStat,USE_DHCP,%u\n", ipconfigUSE_DHCP ) ); /* * Start of fixed RAM allocations. */ STATS_PRINTF( ( "TCPMemStat,\n" ) ); STATS_PRINTF( ( "TCPMemStat,RAM that is always allocated either statically or on the heap:\n" ) ); STATS_PRINTF( ( "TCPMemStat,ipconfig item,Value,PerUnit,Total\n" ) ); xFirstLineNr = xCurrentLine; if( xBufferAllocFixedSize != 0 ) { size_t uxBytes; /* Using BufferAllocation_1.c */ uxPacketSize = ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 31 ) & ~0x1FuL; uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * ( uxPacketSize + sizeof( NetworkBufferDescriptor_t ) ); STATS_PRINTF( ( "TCPMemStat,NUM_NETWORK_BUFFER_DESCRIPTORS,%u,%u,=B%d*C%d,Descriptors + buffers\n", ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, uxPacketSize + sizeof( NetworkBufferDescriptor_t ), xCurrentLine, xCurrentLine ) ); uxStaticSize += uxBytes; } else { size_t uxBytes; /* Using BufferAllocation_2.c */ uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * sizeof( NetworkBufferDescriptor_t ); STATS_PRINTF( ( "TCPMemStat,NUM_NETWORK_BUFFER_DESCRIPTORS,%u,%u,=B%d*C%d,Descriptors only\n", ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, sizeof( NetworkBufferDescriptor_t ), xCurrentLine, xCurrentLine ) ); uxStaticSize += uxBytes; } { #if( ipconfigUSE_TCP_WIN != 0 ) { STATS_PRINTF( ( "TCPMemStat,TCP_WIN_SEG_COUNT,%u,%u,=B%d*C%d\n", ipconfigTCP_WIN_SEG_COUNT, sizeof( TCPSegment_t ), xCurrentLine, xCurrentLine ) ); } #else { STATS_PRINTF( ( "TCPMemStat,TCP_WIN_SEG_COUNT,%u,%u\n", 0, 0 ) ); } #endif } { size_t uxBytes; size_t uxEntrySize; uxBytes = ipconfigEVENT_QUEUE_LENGTH * sizeof( IPStackEvent_t ); STATS_PRINTF( ( "TCPMemStat,EVENT_QUEUE_LENGTH,%u,%u,=B%d*C%d\n", ipconfigEVENT_QUEUE_LENGTH, sizeof( IPStackEvent_t ), xCurrentLine, xCurrentLine ) ); uxStaticSize += uxBytes; uxBytes = ipconfigIP_TASK_STACK_SIZE_WORDS * sizeof( void *); STATS_PRINTF( ( "TCPMemStat,IP_TASK_STACK_SIZE_WORDS,%u,%u,=B%d*C%d\n", ipconfigIP_TASK_STACK_SIZE_WORDS, sizeof( void *), xCurrentLine, xCurrentLine ) ); uxStaticSize += uxBytes; uxBytes = ipconfigARP_CACHE_ENTRIES * sizeof( ARPCacheRow_t ); STATS_PRINTF( ( "TCPMemStat,ARP_CACHE_ENTRIES,%u,%u,=B%d*C%d\n", ipconfigARP_CACHE_ENTRIES, sizeof( ARPCacheRow_t ), xCurrentLine, xCurrentLine ) ); uxStaticSize += uxBytes; #if( ipconfigUSE_DNS_CACHE == 1 ) { uxEntrySize = 3u * sizeof( uint32_t ) + ( ( ipconfigDNS_CACHE_NAME_LENGTH + 3 ) & ~0x3u ); STATS_PRINTF( ( "TCPMemStat,DNS_CACHE_ENTRIES,%u,%u,=B%d*C%d\n", ipconfigDNS_CACHE_ENTRIES, uxEntrySize, xCurrentLine, xCurrentLine ) ); } #endif } /* * End of fixed RAM allocations. */ if( xBufferAllocFixedSize != 0 ) { pucComment[0] = 0; } else { size_t uxBytes; /* BufferAllocation_2.c uses HEAP to store network packets. */ uxPacketSize = ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 3 ) & ~0x03uL; uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * uxPacketSize; STATS_PRINTF( ( "TCPMemStat,Network buffers in HEAP,%u,%u,=B%d*C%d\n", ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, uxPacketSize, xCurrentLine, xCurrentLine ) ); uxStaticSize += uxBytes; snprintf( pucComment, sizeof pucComment, "Actual size fluctuates because BufferAllocation_2.c is used" ); } xLastHeaderLineNr = xCurrentLine; STATS_PRINTF( ( "TCPMemStat,Total,,,=SUM(D%d:D%d),%s\n", xFirstLineNr + 1, xLastHeaderLineNr, pucComment ) ); STATS_PRINTF( ( "TCPMemStat,\n" ) ); STATS_PRINTF( ( "TCPMemStat,\n" ) ); STATS_PRINTF( ( "TCPMemStat,The following allocations are done on the heap while running:\n" ) ); STATS_PRINTF( ( "TCPMemStat,Create/Remove,Object,Size,Heap use,Pointer,Heap-min,Heap-Cur,comment\n" ) ); } /*-----------------------------------------------------------*/ void vTCPMemStatCreate( TCP_MEMORY_t xMemType, void *pxObject, size_t uxSize ) { if( xLoggingStopped == pdFALSE ) { StreamBuffer_t *pxBuffer = NULL; char pcExtra[ 81 ] = ""; if( xFirstItem != pdFALSE ) { xFirstItem = pdFALSE; vWriteHeader(); } if( ( xMemType == tcpRX_STREAM_BUFFER ) || ( xMemType == tcpTX_STREAM_BUFFER ) ) { size_t uxTara = sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray ); size_t uxNett = uxSize - uxTara; snprintf( pcExtra, sizeof pcExtra, ",%u nett", uxNett ); } if( xFirstDumpLine == 0 ) { xFirstDumpLine = xCurrentLine + 1; } xCurrentLine++; configPRINTF( ( "TCPMemStat,CREATE,%s,%lu,%lu,%u,%u,%u%s\n", pcTypeName( xMemType ), uxSize, uxCurrentMallocSize + uxSize, uxNextObjectNumber, xPortGetMinimumEverFreeHeapSize(), xPortGetFreeHeapSize(), pcExtra ) ); uxCurrentMallocSize += uxSize; vAddAllocation( xMemType, pxObject, uxSize ); } } /*-----------------------------------------------------------*/ void vTCPMemStatDelete( void *pxObject ) { if( xLoggingStopped == pdFALSE ) { TCP_ALLOCATION_t *pxFound = pxRemoveAllocation( pxObject ); if( xFirstDumpLine == 0 ) { xFirstDumpLine = xCurrentLine + 1; } if( pxFound == NULL ) { FreeRTOS_printf( ( "TCPMemStat: can not find pointer %p\n", pxObject ) ); } else { xCurrentLine++; configPRINTF( ( "TCPMemStat,REMOVE,%s,-%lu,%lu,%x,%u,%u\n", pcTypeName( pxFound->xMemType ), pxFound->uxSize, uxCurrentMallocSize - pxFound->uxSize, pxFound->uxNumber, xPortGetMinimumEverFreeHeapSize(), xPortGetFreeHeapSize() ) ); if( uxCurrentMallocSize < pxFound->uxSize ) { uxCurrentMallocSize = 0uL; } else { uxCurrentMallocSize -= pxFound->uxSize; } } } } /*-----------------------------------------------------------*/ void vTCPMemStatClose() { if( xLoggingStopped == pdFALSE ) { // name;object;size;Heap;Ppointer;HeapMin;HeapDur;Comment BaseType_t xLastLineNr = xCurrentLine; xLoggingStopped = pdTRUE; STATS_PRINTF( ( "TCPMemStat,Totals,,,=MAX(D%d:D%d),,=MIN(F%d:F%d),=MAX(G%d:G%d)\n", xFirstDumpLine, xLastLineNr, xFirstDumpLine, xLastLineNr, xFirstDumpLine, xLastLineNr ) ); STATS_PRINTF( ( "TCPMemStat,Maximum RAM usage:,,,=SUM(D%d;D%d)\n", xLastHeaderLineNr + 1, xLastLineNr + 1 ) ); } } /*-----------------------------------------------------------*/ #endif /* ( ipconfigUSE_TCP_MEM_STATS != 0 ) */