diff options
author | Hein Tibosch <hein_tibosch@yahoo.es> | 2020-06-25 13:01:45 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-24 22:01:45 -0700 |
commit | 98bfc38bf3404414878dc68ea41753bea4e24c8e (patch) | |
tree | 6c3bcafb3af4b95934419866ccfcb1c8578b940d /FreeRTOS-Plus | |
parent | 072a173c9df31c75ff64bde440f3f316cedb9033 (diff) | |
download | freertos-git-98bfc38bf3404414878dc68ea41753bea4e24c8e.tar.gz |
FreeRTOS+TCP : add memory statistics and dump packets, v3 (#83)
* FreeRTOS+TCP : add memory statistics and dump packets, v3
* Two changes as requested by Aniruddha
Co-authored-by: Hein Tibosch <hein@htibosch.net>
Co-authored-by: Aniruddha Kanhere <60444055+AniruddhaKanhere@users.noreply.github.com>
Diffstat (limited to 'FreeRTOS-Plus')
4 files changed, 1200 insertions, 0 deletions
diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_dump_packets.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_dump_packets.c new file mode 100644 index 000000000..8ac6cdb1f --- /dev/null +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_dump_packets.c @@ -0,0 +1,658 @@ +/* + * 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_dump_packets.c + * Used in the PC/Win project to dump Ethernet packets, along with some description. + * See tools/tcp_dump_packets.md for further description. + */ + +/* Standard includes. */ +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <ctype.h> + +/* FreeRTOS includes. */ +#include <FreeRTOS.h> +#include "task.h" + +/* FreeRTOS+TCP includes. */ +#include "FreeRTOS_IP.h" +#include "FreeRTOS_Sockets.h" +#include "FreeRTOS_Stream_Buffer.h" +#include "FreeRTOS_IP_Private.h" + +#if( ipconfigUSE_DUMP_PACKETS != 0 ) + +#include "tcp_dump_packets.h" + +/* The priority of the windows thread. */ +#define dumpPROCESS_THREAD_PRIORITY THREAD_PRIORITY_NORMAL + +/* There is a stream buffer between the FreeRTOS tasks sending network packets, +and the Windows thread that writes these packets to disk. The macro 'dumpITEM_COUNT' +determines the number of full-size packets that can be stored in this stream buffer. */ +#ifndef dumpITEM_COUNT + #define dumpITEM_COUNT 32 +#endif + +/* Packets are written in hex notation, no more than 16 bytes on a row. */ +#ifndef dumpBYTES_PER_ROW + #define dumpBYTES_PER_ROW 16 +#endif + +/* The TCP port number reserved for a DNS server. */ +#define dnsDNS_PORT 0x0035u + +/* Some const values describing the 'flags' in a TCP packet. */ +#define tcpTCP_FLAG_FIN 0x0001u /* No more data from sender */ +#define tcpTCP_FLAG_SYN 0x0002u /* Synchronize sequence numbers */ +#define tcpTCP_FLAG_RST 0x0004u /* Reset the connection */ +#define tcpTCP_FLAG_PSH 0x0008u /* Push function: please push buffered data to the recv application */ +#define tcpTCP_FLAG_ACK 0x0010u /* Acknowledgment field is significant */ + +/* A macro to add a type, both as a numeric value, as well as a string. */ +#define ADD_TYPE( FLAGS ) \ + vAddType( flag_##FLAGS, #FLAGS ) + +/*-----------------------------------------------------------*/ + +static char pcTypeString[ 255 ]; +static uint32_t ulTypeMask; + +/* The name of the C source file to be written. */ +static char pcCodeFileName[ MAX_PATH ]; + +/* The name of the header file to be written. */ +static char pcHeaderFileName[ MAX_PATH ]; + +/* A stream buffer between the FreeRTOS tasks and the Windows thread. */ +static StreamBuffer_t *xPacketBuffer; + +/* A process handle of the Windows thread. */ +static HANDLE pvProcessHandle; + +static UBaseType_t uxNextPacketNumber; +static BaseType_t xFirstPacket = 1; + +/* Bollean 'xDumpingReady' becomes true once all desired packet have been collected. +Further packets will be dropped (ignored). */ +static volatile BaseType_t xDumpingReady = pdFALSE; + +static DumpEntries_t *pxCurrentEntries; + +static uint16_t usSourcePort; +static uint16_t usDestinationPort; + +typedef struct xBufferheader +{ + size_t uxLength; + UBaseType_t bIncoming : 1; +} Bufferheader_t; + +static DumpEntries_t xExampleEntries = { + .uxEntryCount = 4, /* No more than 'dumpMAX_DUMP_ENTRIES' elements. */ + .xEntries = { + { .ulMask = flag_IN | flag_UDP, .uxMax = 2u }, + { .ulMask = flag_IN | flag_ARP, .uxMax = 2u }, + { .ulMask = flag_IN | flag_TCP, .uxMax = 5u }, + { .ulMask = flag_IN | flag_SYN, .uxMax = 1u }, + } +}; + +const char pcHeaderCode[] = + "/*\n" + " * This file was created automatically by 'dump_packets.c'\n" + " */\n" + "\n" + "/* Standard includes. */\n" + "#include <stdio.h>\n" + "#include <stdint.h>\n" + "#include <stdarg.h>\n" + "#include <io.h>\n" + "#include <ctype.h>\n" + "\n" + "/* FreeRTOS includes. */\n" + "#include <FreeRTOS.h>\n" + "#include <task.h>\n\n" + "#include \"%s\"\n\n"; + +const char pcHeaderHeader[] = + "/*\n" + " * This file was created automatically by 'dump_packets.c'\n" + " */\n" + "\n" + "#ifndef PACKET_LIST_H\n\n" + "#define PACKET_LIST_H\n\n" + "typedef struct xDumpPacket\n" + "{\n" + " const uint8_t *pucData;\n" + " size_t uxLength;\n" + " uint32_t ulType;\n" + " uint16_t usSource;\n" + " uint16_t usDestination;\n" + "} DumpPacket_t;\n\n"; + +/*-----------------------------------------------------------*/ + +/* The Windows thread that actually writes the network packets to a C source and header file. */ +static DWORD WINAPI prvWritePackets( LPVOID lpParameter ); + +static void vAddProtocolTags( uint8_t *pucEthernetBuffer, BaseType_t xIPType ); +static void vDetermineMessageType( uint8_t *pucBuffer, BaseType_t xIncoming ); +static void vActualDump( uint8_t *pucBuffer, size_t uxLength, BaseType_t xIncoming ); +static void vAddType( uint32_t ulFlags, const char *pcFlagName ); +static void vWriteHeaderFile( void ); + +/*-----------------------------------------------------------*/ + +void dump_packet_init( const char *pcFileName, DumpEntries_t *pxEntries ) +{ +size_t uxIndex; + + snprintf( pcCodeFileName, sizeof pcCodeFileName, "%s.c", pcFileName ); + snprintf( pcHeaderFileName, sizeof pcHeaderFileName, "%s.h", pcFileName ); + + if( pxEntries == NULL ) + { + pxEntries = &( xExampleEntries ); + } + configASSERT( pxEntries->uxEntryCount > 0 ); + configASSERT( pxEntries->uxEntryCount <= dumpMAX_DUMP_ENTRIES ); + + for( uxIndex = 0; uxIndex < pxEntries->uxEntryCount; uxIndex++ ) + { + pxEntries->xEntries[ uxIndex ].uxCount = 0; + } + + pxCurrentEntries = pxEntries; + + if( xPacketBuffer == NULL ) + { + size_t uxLength, uxSize; + + /* Enough space for e.g. 32 buffers and length words. */ + uxLength = dumpITEM_COUNT * ( sizeof( void * ) + ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER ); + uxSize = ( sizeof( *xPacketBuffer ) + uxLength ) - sizeof( xPacketBuffer->ucArray ); + xPacketBuffer = ( StreamBuffer_t * ) pvPortMalloc( uxSize ); + configASSERT( xPacketBuffer != NULL ); + vStreamBufferClear( xPacketBuffer ); + xPacketBuffer->LENGTH = uxLength; + } + + if( pvProcessHandle == NULL ) + { + pvProcessHandle = CreateThread( NULL, 0, prvWritePackets, NULL, CREATE_SUSPENDED, NULL ); + if( pvProcessHandle != NULL ) + { + SetThreadPriority( pvProcessHandle, dumpPROCESS_THREAD_PRIORITY ); + SetThreadPriorityBoost( pvProcessHandle, TRUE ); + SetThreadAffinityMask( pvProcessHandle, 0x0E ); + ResumeThread( pvProcessHandle ); + } + } +} +/*-----------------------------------------------------------*/ + +void dump_packet( const uint8_t *pucBuffer, size_t uxLength, BaseType_t xIncoming ) +{ + /* This function shall be called from a normal FreeRTOS task only. */ + if( xPacketBuffer != NULL ) + { + if( xDumpingReady == pdFALSE ) + { + size_t uxSpace = uxStreamBufferGetSpace( xPacketBuffer ); + size_t uxNeeded = uxLength + sizeof( size_t ); + + if( uxNeeded < uxSpace ) + { + Bufferheader_t xheader; + + xheader.uxLength = uxLength; + xheader.bIncoming = xIncoming; + uxStreamBufferAdd( xPacketBuffer, 0u, ( const uint8_t * ) &( xheader ), sizeof( xheader ) ); + uxStreamBufferAdd( xPacketBuffer, 0u, pucBuffer, uxLength ); + } + else + { + /* Drop this packet. */ + } + } + else + { + /* The Windows thread 'prvWritePackets()' had received enough packets. + The packet buffer may be freed. */ + vPortFree( xPacketBuffer ); + xPacketBuffer = NULL; + } + } +} +/*-----------------------------------------------------------*/ + +static DWORD WINAPI prvWritePackets( LPVOID lpParameter ) +{ + /* This is a Windows thread, not a FreeRTOS task. FreeRTOS API's may not be called. */ + for( ;; ) + { + Sleep( 100 ); + + while( ( xPacketBuffer != NULL ) && ( xDumpingReady == pdFALSE ) ) + { + Bufferheader_t xHeader; + size_t uxBytes = uxStreamBufferGetSize( xPacketBuffer ); + + if( uxBytes <= sizeof( xHeader ) ) + break; + + /* Peek the number of bytes available. */ + uxStreamBufferGet( xPacketBuffer, 0u, ( uint8_t * ) &( xHeader ), sizeof( xHeader ), pdTRUE ); + if( uxBytes >= sizeof( xHeader ) + xHeader.uxLength ); + { + size_t xBytesRead; + uint8_t pcBuffer[ ipconfigNETWORK_MTU ]; + size_t xActualCount; + + uxStreamBufferGet( xPacketBuffer, 0u, NULL, sizeof( xHeader ), pdFALSE ); + xActualCount = uxStreamBufferGet( xPacketBuffer, 0u, pcBuffer, xHeader.uxLength, pdFALSE ); + vActualDump( pcBuffer, xActualCount, xHeader.bIncoming ); + } + } + } +} +/*-----------------------------------------------------------*/ + +static int _fprintf( FILE *pxHandle, const char* pcFormat, ... ) +{ +char pcString[ 255 ]; +BaseType_t iCount; + + va_list args; + va_start (args, pcFormat); + iCount = vsnprintf( pcString, sizeof pcString, pcFormat, args); + va_end (args); + fwrite( pcString, 1u, iCount, pxHandle ); + + return iCount; +} +/*-----------------------------------------------------------*/ + +static void vWriteHeaderFile( void ) +{ +FILE *outfile; + + outfile = fopen( pcHeaderFileName, "w" ); + if( outfile != NULL ) + { + fwrite( pcHeaderHeader, 1u, sizeof( pcHeaderHeader ) - 1u, outfile ); + _fprintf( outfile, "#define dumpPACKET_COUNT %lu\n\n", + ( uxNextPacketNumber < 1u ) ? 1u : uxNextPacketNumber ); + _fprintf( outfile, "extern DumpPacket_t *xPacketList[ dumpPACKET_COUNT ];\n\n" ); + _fprintf( outfile, "#endif PACKET_LIST_H\n" ); + + fclose ( outfile ); + } +} +/*-----------------------------------------------------------*/ + +static void vAddType( uint32_t ulFlags, const char *pcFlagName ) +{ +size_t uxLength = strlen( pcTypeString ); +char pcString[ 64 ]; +BaseType_t iCount; + + ulTypeMask |= ulFlags; + + if( uxLength == 0 ) + { + snprintf( pcTypeString, sizeof pcTypeString, "%s", pcFlagName ); + } + else + { + snprintf( pcTypeString + uxLength, sizeof pcTypeString - 1, " | %s", pcFlagName ); + } +} +/*-----------------------------------------------------------*/ + +static void vAddProtocolTags( uint8_t *pucEthernetBuffer, BaseType_t xIPType ) +{ +ProtocolHeaders_t *pxProtocolHeaders; +#if( ipconfigUSE_IPv6 != 0 ) + const IPHeader_IPv6_t * pxIPHeader_IPv6; +#endif +UBaseType_t uxHeaderLength; +uint8_t ucProtocol; +IPPacket_t * pxIPPacket; +IPHeader_t * pxIPHeader; + + pxIPPacket = ( IPPacket_t * ) pucEthernetBuffer; + pxIPHeader = &( pxIPPacket->xIPHeader ); + #if( ipconfigUSE_IPv6 != 0 ) + pxIPHeader_IPv6 = ipPOINTER_CAST( const IPHeader_IPv6_t *, &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER ] ) ); + if( pxIPPacket->xEthernetHeader.usFrameType == ipIPv6_FRAME_TYPE ) + { + uxHeaderLength = ipSIZE_OF_IPv6_HEADER; + ucProtocol = pxIPHeader_IPv6->ucNextHeader; + pxProtocolHeaders = ipPOINTER_CAST( ProtocolHeaders_t *, &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER ] ) ); + } + else + #endif + { + size_t uxLength = ( size_t ) pxIPHeader->ucVersionHeaderLength; + + /* Check if the IP headers are acceptable and if it has our destination. + The lowest four bits of 'ucVersionHeaderLength' indicate the IP-header + length in multiples of 4. */ + uxHeaderLength = ( size_t ) ( ( uxLength & 0x0Fu ) << 2 ); + ucProtocol = pxIPPacket->xIPHeader.ucProtocol; + pxProtocolHeaders = ipPOINTER_CAST( ProtocolHeaders_t *, &( pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxHeaderLength ] ) ); + } + + switch( ucProtocol ) + { + case ipPROTOCOL_ICMP : + ADD_TYPE( ICMP4 ); + break; + +#if( ipconfigUSE_IPv6 != 0 ) + case ipPROTOCOL_ICMP_IPv6: + ADD_TYPE( ICMP6 ); + break; +#endif + + case ipPROTOCOL_UDP : + { + ADD_TYPE( UDP ); + usSourcePort = pxProtocolHeaders->xUDPHeader.usSourcePort; + usDestinationPort = pxProtocolHeaders->xUDPHeader.usDestinationPort; + if( usSourcePort == FreeRTOS_htons( dnsDNS_PORT) ) + { + ADD_TYPE( DNS ); + ADD_TYPE( REPLY ); + } + else if( usDestinationPort == FreeRTOS_htons( dnsDNS_PORT) ) + { + ADD_TYPE( DNS ); + ADD_TYPE( REQUEST ); + } + } + break; +#if ipconfigUSE_TCP == 1 + case ipPROTOCOL_TCP : + { + ADD_TYPE( TCP ); + usSourcePort = pxProtocolHeaders->xTCPHeader.usSourcePort; + usDestinationPort = pxProtocolHeaders->xTCPHeader.usDestinationPort; + if( ( pxProtocolHeaders->xTCPHeader.ucTCPFlags & tcpTCP_FLAG_SYN ) != 0u ) + { + ADD_TYPE( SYN ); + } + if( ( pxProtocolHeaders->xTCPHeader.ucTCPFlags & tcpTCP_FLAG_FIN ) != 0u ) + { + ADD_TYPE( FIN ); + } + if( ( pxProtocolHeaders->xTCPHeader.ucTCPFlags & tcpTCP_FLAG_RST ) != 0u ) + { + ADD_TYPE( RST ); + } + if( ( pxProtocolHeaders->xTCPHeader.ucTCPFlags & tcpTCP_FLAG_ACK ) != 0u ) + { + ADD_TYPE( ACK ); + } + } + break; +#endif + } +} +/*-----------------------------------------------------------*/ + +static void vDetermineMessageType( uint8_t *pucBuffer, BaseType_t xIncoming ) +{ +EthernetHeader_t *pxEthernetHeader; + + if( xIncoming != 0 ) + { + ADD_TYPE( IN ); + } + else + { + ADD_TYPE( OUT ); + } + pxEthernetHeader = ( EthernetHeader_t * ) pucBuffer; + + /* Interpret the received Ethernet packet. */ + switch( pxEthernetHeader->usFrameType ) + { + case ipARP_FRAME_TYPE : + { + ARPPacket_t * pxARPFrame; + ARPHeader_t *pxARPHeader; + + /* The Ethernet frame contains an ARP packet. */ + ADD_TYPE( FRAME_ARP ); + pxARPFrame = ( ARPPacket_t * ) pucBuffer; + pxARPHeader = &( pxARPFrame->xARPHeader ); + ADD_TYPE( ARP ); + switch( pxARPHeader->usOperation ) + { + case ipARP_REQUEST: + ADD_TYPE( REQUEST ); + break; + case ipARP_REPLY: + ADD_TYPE( REPLY ); + break; + default: + ADD_TYPE( UNKNOWN ); + break; + } + } + break; + + case ipIPv4_FRAME_TYPE : + { + ADD_TYPE( FRAME_4 ); + vAddProtocolTags( pucBuffer, 4 ); + } + break; + + #if( ipconfigUSE_IPv6 != 0 ) + case ipIPv6_FRAME_TYPE : + { + ADD_TYPE( FRAME_6 ); + vAddProtocolTags( pucBuffer, 6 ); + } + break; + #endif + default : + /* No other packet types are handled. Nothing to do. */ + ADD_TYPE( Unknown_FRAME ); + break; + } +} +/*-----------------------------------------------------------*/ + +static void vActualDump( uint8_t *pucBuffer, size_t uxLength, BaseType_t xIncoming ) +{ +char pcString[ 513 ]; +size_t uxOffset; +size_t uxIndex; +size_t uxCompleteCount = 0; +BaseType_t xUseIt = pdFALSE; + + usSourcePort = 0u; + usDestinationPort = 0u; + pcTypeString[ 0 ] = 0; + ulTypeMask = 0uL; + + if( pxCurrentEntries == NULL ) + { + return; + } + + vDetermineMessageType( pucBuffer, xIncoming ); + + for( uxIndex = 0; uxIndex < pxCurrentEntries->uxEntryCount; uxIndex++ ) + { + if( pxCurrentEntries->xEntries[ uxIndex ].uxCount < pxCurrentEntries->xEntries[ uxIndex ].uxMax ) + { + uint32_t ulMask = pxCurrentEntries->xEntries[ uxIndex ].ulMask; + + if( ( ulMask & ulTypeMask ) == ulMask ) + { + pxCurrentEntries->xEntries[ uxIndex ].uxCount++; + xUseIt = pdTRUE; + } + } + else + { + uxCompleteCount++; + } + } + FreeRTOS_printf( ( "prvWritePackets: done %d/%d : (%d,%d) (%d,%d) (%d,%d) (%d,%d)\n", + uxCompleteCount, + pxCurrentEntries->uxEntryCount, + pxCurrentEntries->xEntries[ 0 ].uxCount, pxCurrentEntries->xEntries[ 0 ].uxMax, + pxCurrentEntries->xEntries[ 1 ].uxCount, pxCurrentEntries->xEntries[ 1 ].uxMax, + pxCurrentEntries->xEntries[ 2 ].uxCount, pxCurrentEntries->xEntries[ 2 ].uxMax, + pxCurrentEntries->xEntries[ 3 ].uxCount, pxCurrentEntries->xEntries[ 3 ].uxMax ) ); + if( uxCompleteCount >= pxCurrentEntries->uxEntryCount ) + { + FreeRTOS_printf( ( "prvWritePackets: all %lu packets have been collected\n", pxCurrentEntries->uxEntryCount ) ); + if( pxCurrentEntries != NULL ) + { + FILE *outfile = fopen( pcCodeFileName, ( xFirstPacket != 0 ) ? "w" : "a+" ); + if ( outfile == NULL ) + { + FreeRTOS_printf( ( "Can not create '%s'\n", pcCodeFileName ) ); + } + else + { + /* + Create a list with pointers to each network packet. + DumpPacket_t *xPacketList[ dumpPACKET_COUNT ] = + { + &xPacket_0000, + &xPacket_0001, + &xPacket_0002, + &xPacket_0003, + } + */ + _fprintf( outfile, "\nDumpPacket_t *xPacketList[ dumpPACKET_COUNT ] =\n{\n" ); + for( uxIndex = 0; uxIndex < uxNextPacketNumber; uxIndex++ ) + { + _fprintf( outfile, "\t&xPacket_%04lu,\n", uxIndex ); + } + _fprintf( outfile, "};\n" ); + fclose( outfile ); + vWriteHeaderFile(); + } + pxCurrentEntries = NULL; + /* Tell the thread and the function dump_packet() that packet + dumping is ready. */ + xDumpingReady = pdTRUE; + } + return; + } + if( xUseIt == pdFALSE ) + { + return; + } + + printf("prvWritePackets: Read %lu bytes, type %s\n", uxLength, pcTypeString ); + + FILE *outfile = fopen( pcCodeFileName, ( xFirstPacket != 0 ) ? "w" : "a+" ); + if ( outfile == NULL ) + { + FreeRTOS_printf( ( "Can not create '%s'\n", pcCodeFileName ) ); + return; + } + if( xFirstPacket != 0 ) + { + char *pcPtr; + size_t xLength; + + vWriteHeaderFile( pcHeaderFileName ); + xLength = snprintf( pcString, sizeof pcString, pcHeaderCode, pcHeaderFileName ); + fwrite( pcString, 1u, xLength, outfile ); + xFirstPacket = pdFALSE; + } + + _fprintf( outfile, "\n/* Packet_%04d */\n", uxNextPacketNumber ); + _fprintf( outfile, "uint8_t ucPacket_%04lx[ %lu ] =\n{\n", uxNextPacketNumber, uxLength ); + + for( uxOffset = 0u; uxOffset < uxLength; ) + { + size_t uxCurLength = 0u; + size_t uxLast = uxOffset + dumpBYTES_PER_ROW; + BaseType_t xFirst = pdTRUE; + + if( uxLast > uxLength ) + { + uxLast = uxLength; + } + while( uxOffset < uxLast ) + { + uxCurLength += snprintf( pcString + uxCurLength, sizeof pcString - uxCurLength, "%s0x%02x", + ( uxCurLength == 0 ) ? "\t" : ", ", pucBuffer[ uxOffset ] ); + uxOffset++; + } + if( uxCurLength != 0u ) + { + uxCurLength += snprintf( pcString + uxCurLength, sizeof pcString - uxCurLength, "%s\n", + ( uxOffset == uxLength ) ? "\n};" : "," ); + fwrite( pcString, 1u, uxCurLength, outfile ); + } + } + + _fprintf( outfile, "\n"); + + _fprintf( outfile, + "DumpPacket_t xPacket_%04lx =\n{\n" + "\t.pucData = ucPacket_%04lx,\n" + "\t.uxLength = %lu,\n" + "\t.ulType = 0x%lX, /* %s */\n", + uxNextPacketNumber, uxNextPacketNumber, uxLength, ulTypeMask, pcTypeString ); + + if( usSourcePort != 0u ) + { + _fprintf( outfile, + "\t.usSource = %u,\n", FreeRTOS_ntohs( usSourcePort ) ); + } + if( usSourcePort != 0u ) + { + _fprintf( outfile, + "\t.usDestination = %u,\n", FreeRTOS_ntohs( usDestinationPort ) ); + } + + _fprintf( outfile, + "};\n" + "/*-----------------------------------------------------------*/\n\n" ); + fclose( outfile ); + uxNextPacketNumber++; +} +/*-----------------------------------------------------------*/ + +#endif /* ( ipconfigUSE_DUMP_PACKETS != 0 ) */ + diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_dump_packets.md b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_dump_packets.md new file mode 100644 index 000000000..e2b56f705 --- /dev/null +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_dump_packets.md @@ -0,0 +1,72 @@ +tcp_dump_packets.c dumps network packets in a C source file. + +It is written to be added to the "pc" project ( Windows simulator ). It uses the file system to write 2 C source files: + + PacketList.c + PacketList.h + +How to include 'tcp_dump_packets' into a project: + +● Make sure that tools/tcp_dump_packets.c is added to the source files +● See if Network Interface has been adapted to call: + `iptraceDUMP_PACKET( ucBuffer, xLength, pdTRUE ); /* Incoming packet. */` + `iptraceDUMP_PACKET( ucBuffer, xLength, pdFALSE ); /* Outgoing packet. */` +● Once the network is up, call `dump_packet_init()` with a file name and a pointer to + `DumpEntries_t`, which contains the requirements. For instance like this: + static DumpEntries_t xExampleEntries = { + .uxEntryCount = 4, /* No more than 'dumpMAX_DUMP_ENTRIES' elements. */ + .xEntries = { + { .ulMask = flag_IN | flag_UDP, .uxMax = 2u }, + { .ulMask = flag_IN | flag_ARP, .uxMax = 2u }, + { .ulMask = flag_IN | flag_TCP, .uxMax = 5u }, + { .ulMask = flag_IN | flag_SYN, .uxMax = 1u }, + } + }; +● Add the following lines to FreeRTOSIPConfig.h : + #define ipconfigUSE_DUMP_PACKETS ( 1 ) + #include "../tools/tcp_dump_packets.h" + +Later on, the module can disabled by simply setting `ipconfigUSE_DUMP_PACKETS` to `0`. + +Here is some contents of the output file: + + /* Packet_0001 */ + uint8_t ucPacket_0001[ 60 ] = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x74, 0xb5, 0x7e, 0xf0, 0x47, 0xee, 0x08, 0x06, 0x00, 0x01, + 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x74, 0xb5, 0x7e, 0xf0, 0x47, 0xee, 0xc0, 0xa8, 0x02, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + DumpPacket_t xPacket_0001 = + { + .pucData = ucPacket_0001, + .uxLength = 60, + .ulType = 0x6840, /* IN | FRAME_ARP | ARP | REQUEST */ + }; + /*-----------------------------------------------------------*/ + +tcp_dump_packets has an enum of all possible properties of network packets: + ICMP4, ICMP6, UDP, TCP, DNS, REPLY, REQUEST, SYN, + FIN, RST, ACK, IN, OUT, ARP, FRAME_ARP, FRAME_4, and FRAME_6 + +Each property is defined as a bit so they can be combined as in: + .ulType = 0x6840, /* IN | FRAME_ARP | ARP | REQUEST */ + +Finishing: when there are enough packets of all required types, an array is added to the C-source output: + + DumpPacket_t *xPacketList[ dumpPACKET_COUNT ] = + { + &xPacket_0000, + &xPacket_0001, + &xPacket_0002, + &xPacket_0003, + &xPacket_0004, + &xPacket_0005, + &xPacket_0006, + &xPacket_0007, + &xPacket_0008, + }; + +The new source file PacketList.{c, h} can be used in testing software as sample packets. diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_mem_stats.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_mem_stats.c new file mode 100644 index 000000000..d4929b3fa --- /dev/null +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_mem_stats.c @@ -0,0 +1,425 @@ +/* + * 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 <stdio.h> +#include <stdint.h> +#include <stdarg.h> + +/* FreeRTOS includes. */ +#include <FreeRTOS.h> +#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 ) */ diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_mem_stats.md b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_mem_stats.md new file mode 100644 index 000000000..5b638ef4e --- /dev/null +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_mem_stats.md @@ -0,0 +1,45 @@ +tcp_mem_stats.c : FreeRTOS+TCP Memory Statistics + +This module can be used in any project on any platform that uses FreeRTOS+TCP. + +It creates an overview of the memory usage of FreeRTOS+TCP. +It reports the static use of RAM, and also the dynamic usage ( heap ). +It relates these numbers to the macro's defined `FreeRTOSIPConfig.h`. + +It writes CSV records to the logging with configPRINTF(). + +The resulting log can be filtered by e.g.: + + cat logging.txt | grep ".*TCPMemStat," | sed "s/.*TCPMemStat,//" + +The resulting text can be imported into any spreadsheet at cell "A1" ( top-left ): + + ipconfig item,Value,PerUnit,Total + NUM_NETWORK_BUFFER_DESCRIPTORS,12,60,=B13*C13,Descriptors only + TCP_WIN_SEG_COUNT,32,64,=B14*C14 + EVENT_QUEUE_LENGTH,17,8,=B15*C15 + IP_TASK_STACK_SIZE_WORDS,300,4,=B16*C16 + +When the CSV-records are imported at another row or column than "A1", the formulas will be incorrect. + +How to include 'tcp_mem_stats' into a project: + +● Add tools/tcp_mem_stats.c to the sources +● Add the following lines to FreeRTOSIPConfig.h : + #define ipconfigUSE_TCP_MEM_STATS ( 1 ) + #define ipconfigTCP_MEM_STATS_MAX_ALLOCATION ( 128 ) + #include "../tools/tcp_mem_stats.h" + +Later on, the module can disabled by setting `#define ipconfigUSE_TCP_MEM_STATS 0`. + +`ipconfigTCP_MEM_STATS_MAX_ALLOCATION` is the maximum number of objects that can be followed at any time. +A socket that has 2 stream buffers counts as 3 objects ( needing 3 x 16 = 48 bytes to store their properties ). + +The **summary** at the bottom will only be written when `iptraceMEM_STATS_CLOSE()` is called. +The application is responsible for calling `iptraceMEM_STATS_CLOSE()`. +The summary at the bottom looks like this: + + Totals,,,=MAX(D25:D31),,=MIN(F25:F31),=MAX(G25:G31) + Maximum RAM usage:,,,=SUM(D20;D32) + +The spreadsheet can be edited further to make estimations with different macro values. |