summaryrefslogtreecommitdiff
path: root/FreeRTOS-Plus
diff options
context:
space:
mode:
authorHein Tibosch <hein_tibosch@yahoo.es>2020-06-25 13:01:45 +0800
committerGitHub <noreply@github.com>2020-06-24 22:01:45 -0700
commit98bfc38bf3404414878dc68ea41753bea4e24c8e (patch)
tree6c3bcafb3af4b95934419866ccfcb1c8578b940d /FreeRTOS-Plus
parent072a173c9df31c75ff64bde440f3f316cedb9033 (diff)
downloadfreertos-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')
-rw-r--r--FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_dump_packets.c658
-rw-r--r--FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_dump_packets.md72
-rw-r--r--FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_mem_stats.c425
-rw-r--r--FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/tools/tcp_mem_stats.md45
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.