1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 /*
29  * @file tcp_mem_stats.c
30  * @brief To create a CSV file with detailed information about the memory usage of FreeRTOS+TCP.
31  * See tools/tcp_mem_stats.md for further description.
32  */
33 
34 /* Standard includes. */
35 #include <stdio.h>
36 #include <stdint.h>
37 #include <stdarg.h>
38 
39 /* FreeRTOS includes. */
40 #include <FreeRTOS.h>
41 #include "task.h"
42 
43 /* FreeRTOS+TCP includes. */
44 #include "FreeRTOS_IP.h"
45 #include "FreeRTOS_Sockets.h"
46 #include "FreeRTOS_Stream_Buffer.h"
47 #include "FreeRTOS_ARP.h"
48 #include "FreeRTOS_IP_Private.h"
49 
50 #include "tcp_mem_stats.h"
51 
52 #if ( ipconfigUSE_TCP_MEM_STATS != 0 )
53 
54     #ifndef ipconfigTCP_MEM_STATS_MAX_ALLOCATION
55         #define ipconfigTCP_MEM_STATS_MAX_ALLOCATION    128u
56         #pragma warning "ipconfigTCP_MEM_STATS_MAX_ALLOCATION undefined?"
57     #endif
58 
59 /* When a streambuffer is allocated, 4 extra bytes will be reserved. */
60 
61     #define STREAM_BUFFER_ROUNDUP_BYTES    4
62 
63     #define STATS_PRINTF( MSG ) \
64     xCurrentLine++;             \
65     configPRINTF( MSG )
66 
67     #define ETH_MAX_PACKET_SIZE    ( ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 31 ) & ~0x1FuL )
68 /*-----------------------------------------------------------*/
69 
70 /* Objects are allocated and deleted. This structure stores the type
71  * and the size of the object. */
72     typedef struct xTCP_ALLOCATION
73     {
74         TCP_MEMORY_t xMemType;
75         void * pxObject;
76         UBaseType_t uxNumber;
77         size_t uxSize;
78     } TCP_ALLOCATION_t;
79 /*-----------------------------------------------------------*/
80 
81 
82     static void vWriteHeader( void );
83 
84     static size_t uxCurrentMallocSize;
85     static TCP_ALLOCATION_t xAllocations[ ipconfigTCP_MEM_STATS_MAX_ALLOCATION ];
86     static size_t uxAllocationCount;
87     static BaseType_t xFirstItem = pdTRUE;
88     UBaseType_t uxNextObjectNumber;
89     static BaseType_t xCurrentLine = 0;
90     static BaseType_t xFirstDumpLine = 0;
91     static BaseType_t xLastHeaderLineNr = 0;
92     static BaseType_t xLoggingStopped = 0;
93 /*-----------------------------------------------------------*/
94 
vAddAllocation(TCP_MEMORY_t xMemType,void * pxObject,size_t uxSize)95     static void vAddAllocation( TCP_MEMORY_t xMemType,
96                                 void * pxObject,
97                                 size_t uxSize )
98     {
99         size_t uxIndex;
100 
101         vTaskSuspendAll();
102         {
103             for( uxIndex = 0; uxIndex < uxAllocationCount; uxIndex++ )
104             {
105                 if( xAllocations[ uxIndex ].pxObject == pxObject )
106                 {
107                     /* Already added, strange. */
108                     FreeRTOS_printf( ( "vAddAllocation: Pointer %p already added\n", pxObject ) );
109                     return;
110                 }
111             }
112 
113             if( uxAllocationCount >= ipconfigTCP_MEM_STATS_MAX_ALLOCATION )
114             {
115                 /* The table is full. */
116                 return;
117             }
118 
119             xAllocations[ uxIndex ].pxObject = pxObject;
120             xAllocations[ uxIndex ].xMemType = xMemType;
121             xAllocations[ uxIndex ].uxSize = uxSize;
122             xAllocations[ uxIndex ].uxNumber = uxNextObjectNumber++;
123             uxAllocationCount++;
124         }
125         xTaskResumeAll();
126     }
127 /*-----------------------------------------------------------*/
128 
pxRemoveAllocation(void * pxObject)129     static TCP_ALLOCATION_t * pxRemoveAllocation( void * pxObject )
130     {
131         size_t uxSource = 0, uxTarget = 0;
132         static TCP_ALLOCATION_t xAllocation = { 0 };
133         BaseType_t xFound = pdFALSE;
134         TCP_ALLOCATION_t * pxReturn;
135 
136         vTaskSuspendAll();
137         {
138             for( ; uxSource < uxAllocationCount; uxSource++ )
139             {
140                 if( xAllocations[ uxSource ].pxObject == pxObject )
141                 {
142                     /* This is entry will be removed. */
143                     ( void ) memcpy( &( xAllocation ), &( xAllocations[ uxSource ] ), sizeof xAllocation );
144                     xFound = pdTRUE;
145                 }
146                 else
147                 {
148                     xAllocations[ uxTarget ] = xAllocations[ uxSource ];
149                     uxTarget++;
150                 }
151             }
152 
153             if( xFound != pdFALSE )
154             {
155                 uxAllocationCount--;
156                 pxReturn = &( xAllocation );
157             }
158             else
159             {
160                 pxReturn = NULL;
161             }
162         }
163         xTaskResumeAll();
164         return pxReturn;
165     }
166 /*-----------------------------------------------------------*/
167 
pcTypeName(TCP_MEMORY_t xMemType)168     static const char * pcTypeName( TCP_MEMORY_t xMemType )
169     {
170         switch( xMemType )
171         {
172             case tcpSOCKET_TCP:
173                 return "TCP-Socket";
174 
175             case tcpSOCKET_UDP:
176                 return "UDP-Socket";
177 
178             case tcpSOCKET_SET:
179                 return "SocketSet";
180 
181             case tcpSEMAPHORE:
182                 return "Semaphore";
183 
184             case tcpRX_STREAM_BUFFER:
185                 return "RX-Buffer";
186 
187             case tcpTX_STREAM_BUFFER:
188                 return "TX-Buffer";
189 
190             case tcpNETWORK_BUFFER:
191                 return "networkBuffer";
192         }
193 
194         return "Unknown object";
195     }
196 /*-----------------------------------------------------------*/
197 
vWriteHeader()198     static void vWriteHeader()
199     {
200         size_t uxPacketSize;
201         size_t uxTXSize;
202         size_t uxStaticSize = 0;
203         BaseType_t xFirstLineNr = 0;
204 
205         char pucComment[ 64 ] = "";
206         StreamBuffer_t * pxBuffer = NULL;
207         size_t uxTara = sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray );
208 
209         /* The approximate size of a buffer for a Network Packet. */
210         STATS_PRINTF( ( "TCPMemStat,Some important ipconfig items:\n" ) );
211         STATS_PRINTF( ( "TCPMemStat,ipconfig item,Value,Comment\n" ) );
212         STATS_PRINTF( ( "TCPMemStat,NETWORK_MTU,%u\n", ipconfigNETWORK_MTU ) );
213         STATS_PRINTF( ( "TCPMemStat,TCP_MSS,%u\n", ipconfigTCP_MSS ) );
214         STATS_PRINTF( ( "TCPMemStat,USE_TCP,%u\n", ipconfigUSE_TCP ) );
215         STATS_PRINTF( ( "TCPMemStat,USE_TCP_WIN,%u\n", ipconfigUSE_TCP_WIN ) );
216 
217         uxTXSize = ( size_t ) FreeRTOS_round_up( ipconfigTCP_TX_BUFFER_LENGTH, ipconfigTCP_MSS );
218 
219         STATS_PRINTF( ( "TCPMemStat,TCP_RX_BUFFER_LENGTH,%u,Plus %u bytes\n", ipconfigTCP_RX_BUFFER_LENGTH, uxTara + STREAM_BUFFER_ROUNDUP_BYTES ) );
220 
221         if( uxTXSize > ipconfigTCP_TX_BUFFER_LENGTH )
222         {
223             snprintf( pucComment, sizeof pucComment, ",Rounded up to %u x MSS (plus %u bytes)", uxTXSize / ipconfigTCP_MSS, uxTara + STREAM_BUFFER_ROUNDUP_BYTES );
224         }
225 
226         STATS_PRINTF( ( "TCPMemStat,TCP_TX_BUFFER_LENGTH,%u%s\n", ipconfigTCP_TX_BUFFER_LENGTH, pucComment ) );
227         STATS_PRINTF( ( "TCPMemStat,USE_DHCP,%u\n", ipconfigUSE_DHCP ) );
228 
229         /*
230          * Start of fixed RAM allocations.
231          */
232 
233         STATS_PRINTF( ( "TCPMemStat,\n" ) );
234         STATS_PRINTF( ( "TCPMemStat,RAM that is always allocated either statically or on the heap:\n" ) );
235         STATS_PRINTF( ( "TCPMemStat,ipconfig item,Value,PerUnit,Total\n" ) );
236         xFirstLineNr = xCurrentLine;
237 
238         if( xBufferAllocFixedSize != 0 )
239         {
240             size_t uxBytes;
241 
242             /* Using BufferAllocation_1.c */
243             uxPacketSize = ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 31 ) & ~0x1FuL;
244             uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * ( uxPacketSize + sizeof( NetworkBufferDescriptor_t ) );
245 
246             STATS_PRINTF( ( "TCPMemStat,NUM_NETWORK_BUFFER_DESCRIPTORS,%u,%u,=B%d*C%d,Descriptors + buffers\n",
247                             ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS,
248                             uxPacketSize + sizeof( NetworkBufferDescriptor_t ),
249                             xCurrentLine,
250                             xCurrentLine ) );
251             uxStaticSize += uxBytes;
252         }
253         else
254         {
255             size_t uxBytes;
256 
257             /* Using BufferAllocation_2.c */
258             uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * sizeof( NetworkBufferDescriptor_t );
259             STATS_PRINTF( ( "TCPMemStat,NUM_NETWORK_BUFFER_DESCRIPTORS,%u,%u,=B%d*C%d,Descriptors only\n",
260                             ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS,
261                             sizeof( NetworkBufferDescriptor_t ),
262                             xCurrentLine,
263                             xCurrentLine ) );
264             uxStaticSize += uxBytes;
265         }
266 
267         {
268             #if ( ipconfigUSE_TCP_WIN != 0 )
269                 {
270                     STATS_PRINTF( ( "TCPMemStat,TCP_WIN_SEG_COUNT,%u,%u,=B%d*C%d\n",
271                                     ipconfigTCP_WIN_SEG_COUNT, sizeof( TCPSegment_t ), xCurrentLine, xCurrentLine ) );
272                 }
273             #else
274                 {
275                     STATS_PRINTF( ( "TCPMemStat,TCP_WIN_SEG_COUNT,%u,%u\n", 0, 0 ) );
276                 }
277             #endif
278         }
279         {
280             size_t uxBytes;
281             size_t uxEntrySize;
282 
283             uxBytes = ipconfigEVENT_QUEUE_LENGTH * sizeof( IPStackEvent_t );
284             STATS_PRINTF( ( "TCPMemStat,EVENT_QUEUE_LENGTH,%u,%u,=B%d*C%d\n",
285                             ipconfigEVENT_QUEUE_LENGTH,
286                             sizeof( IPStackEvent_t ),
287                             xCurrentLine,
288                             xCurrentLine ) );
289             uxStaticSize += uxBytes;
290 
291             uxBytes = ipconfigIP_TASK_STACK_SIZE_WORDS * sizeof( void * );
292             STATS_PRINTF( ( "TCPMemStat,IP_TASK_STACK_SIZE_WORDS,%u,%u,=B%d*C%d\n",
293                             ipconfigIP_TASK_STACK_SIZE_WORDS,
294                             sizeof( void * ),
295                             xCurrentLine,
296                             xCurrentLine ) );
297             uxStaticSize += uxBytes;
298 
299             uxBytes = ipconfigARP_CACHE_ENTRIES * sizeof( ARPCacheRow_t );
300             STATS_PRINTF( ( "TCPMemStat,ARP_CACHE_ENTRIES,%u,%u,=B%d*C%d\n",
301                             ipconfigARP_CACHE_ENTRIES,
302                             sizeof( ARPCacheRow_t ),
303                             xCurrentLine,
304                             xCurrentLine ) );
305             uxStaticSize += uxBytes;
306 
307             #if ( ipconfigUSE_DNS_CACHE == 1 )
308                 {
309                     uxEntrySize = 3u * sizeof( uint32_t ) + ( ( ipconfigDNS_CACHE_NAME_LENGTH + 3 ) & ~0x3u );
310                     STATS_PRINTF( ( "TCPMemStat,DNS_CACHE_ENTRIES,%u,%u,=B%d*C%d\n",
311                                     ipconfigDNS_CACHE_ENTRIES,
312                                     uxEntrySize,
313                                     xCurrentLine,
314                                     xCurrentLine ) );
315                 }
316             #endif
317         }
318 
319         /*
320          * End of fixed RAM allocations.
321          */
322         if( xBufferAllocFixedSize != 0 )
323         {
324             pucComment[ 0 ] = 0;
325         }
326         else
327         {
328             size_t uxBytes;
329 
330             /* BufferAllocation_2.c uses HEAP to store network packets. */
331             uxPacketSize = ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 3 ) & ~0x03uL;
332             uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * uxPacketSize;
333             STATS_PRINTF( ( "TCPMemStat,Network buffers in HEAP,%u,%u,=B%d*C%d\n",
334                             ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS,
335                             uxPacketSize,
336                             xCurrentLine,
337                             xCurrentLine ) );
338             uxStaticSize += uxBytes;
339             snprintf( pucComment, sizeof pucComment, "Actual size fluctuates because BufferAllocation_2.c is used" );
340         }
341 
342         xLastHeaderLineNr = xCurrentLine;
343 
344         STATS_PRINTF( ( "TCPMemStat,Total,,,=SUM(D%d:D%d),%s\n", xFirstLineNr + 1, xLastHeaderLineNr, pucComment ) );
345 
346         STATS_PRINTF( ( "TCPMemStat,\n" ) );
347         STATS_PRINTF( ( "TCPMemStat,\n" ) );
348         STATS_PRINTF( ( "TCPMemStat,The following allocations are done on the heap while running:\n" ) );
349         STATS_PRINTF( ( "TCPMemStat,Create/Remove,Object,Size,Heap use,Pointer,Heap-min,Heap-Cur,comment\n" ) );
350     }
351 /*-----------------------------------------------------------*/
352 
vTCPMemStatCreate(TCP_MEMORY_t xMemType,void * pxObject,size_t uxSize)353     void vTCPMemStatCreate( TCP_MEMORY_t xMemType,
354                             void * pxObject,
355                             size_t uxSize )
356     {
357         if( xLoggingStopped == pdFALSE )
358         {
359             StreamBuffer_t * pxBuffer = NULL;
360             char pcExtra[ 81 ] = "";
361 
362             if( xFirstItem != pdFALSE )
363             {
364                 xFirstItem = pdFALSE;
365                 vWriteHeader();
366             }
367 
368             if( ( xMemType == tcpRX_STREAM_BUFFER ) || ( xMemType == tcpTX_STREAM_BUFFER ) )
369             {
370                 size_t uxTara = sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray );
371                 size_t uxNett = uxSize - uxTara;
372 
373                 snprintf( pcExtra, sizeof pcExtra, ",%u nett", uxNett );
374             }
375 
376             if( xFirstDumpLine == 0 )
377             {
378                 xFirstDumpLine = xCurrentLine + 1;
379             }
380 
381             xCurrentLine++;
382             configPRINTF( ( "TCPMemStat,CREATE,%s,%lu,%lu,%u,%u,%u%s\n",
383                             pcTypeName( xMemType ),
384                             uxSize,
385                             uxCurrentMallocSize + uxSize,
386                             uxNextObjectNumber,
387                             xPortGetMinimumEverFreeHeapSize(),
388                             xPortGetFreeHeapSize(),
389                             pcExtra ) );
390             uxCurrentMallocSize += uxSize;
391             vAddAllocation( xMemType, pxObject, uxSize );
392         }
393     }
394 /*-----------------------------------------------------------*/
395 
vTCPMemStatDelete(void * pxObject)396     void vTCPMemStatDelete( void * pxObject )
397     {
398         if( xLoggingStopped == pdFALSE )
399         {
400             TCP_ALLOCATION_t * pxFound = pxRemoveAllocation( pxObject );
401 
402             if( xFirstDumpLine == 0 )
403             {
404                 xFirstDumpLine = xCurrentLine + 1;
405             }
406 
407             if( pxFound == NULL )
408             {
409                 FreeRTOS_printf( ( "TCPMemStat: can not find pointer %p\n", pxObject ) );
410             }
411             else
412             {
413                 xCurrentLine++;
414                 configPRINTF( ( "TCPMemStat,REMOVE,%s,-%lu,%lu,%x,%u,%u\n",
415                                 pcTypeName( pxFound->xMemType ),
416                                 pxFound->uxSize,
417                                 uxCurrentMallocSize - pxFound->uxSize,
418                                 pxFound->uxNumber,
419                                 xPortGetMinimumEverFreeHeapSize(),
420                                 xPortGetFreeHeapSize() ) );
421 
422                 if( uxCurrentMallocSize < pxFound->uxSize )
423                 {
424                     uxCurrentMallocSize = 0uL;
425                 }
426                 else
427                 {
428                     uxCurrentMallocSize -= pxFound->uxSize;
429                 }
430             }
431         }
432     }
433 /*-----------------------------------------------------------*/
434 
vTCPMemStatClose()435     void vTCPMemStatClose()
436     {
437         if( xLoggingStopped == pdFALSE )
438         {
439             /* name;object;size;Heap;Ppointer;HeapMin;HeapDur;Comment */
440             BaseType_t xLastLineNr = xCurrentLine;
441 
442             xLoggingStopped = pdTRUE;
443 
444             STATS_PRINTF( ( "TCPMemStat,Totals,,,=MAX(D%d:D%d),,=MIN(F%d:F%d),=MAX(G%d:G%d)\n",
445                             xFirstDumpLine,
446                             xLastLineNr,
447                             xFirstDumpLine,
448                             xLastLineNr,
449                             xFirstDumpLine,
450                             xLastLineNr ) );
451             STATS_PRINTF( ( "TCPMemStat,Maximum RAM usage:,,,=SUM(D%d;D%d)\n",
452                             xLastHeaderLineNr + 1,
453                             xLastLineNr + 1 ) );
454         }
455     }
456 /*-----------------------------------------------------------*/
457 
458 #endif /* ( ipconfigUSE_TCP_MEM_STATS != 0 ) */
459