1 /*
2  * FreeRTOS+TCP V3.1.0
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  * tcp_mem_stats.c
30  * Used 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 
56 /* Define the maximum number of objects ( memory allocations by
57  * the IP-stack ) that will be recorded. */
58         #define ipconfigTCP_MEM_STATS_MAX_ALLOCATION    128u
59 
60 /* If you don't want to see this pragma message, you can either
61  * remove it or define 'ipconfigTCP_MEM_STATS_MAX_ALLOCATION' in
62  * your freeRTOSIPConfig.h. */
63         #pragma message ("ipconfigTCP_MEM_STATS_MAX_ALLOCATION undefined?")
64     #endif
65 
66 /* When a streambuffer is allocated, 4 extra bytes will be reserved. */
67 
68     #define STREAM_BUFFER_ROUNDUP_BYTES    4
69 
70     #define STATS_PRINTF( MSG ) \
71     xCurrentLine++;             \
72     configPRINTF( MSG )
73 
74     #define ETH_MAX_PACKET_SIZE    ( ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 31 ) & ~0x1FuL )
75 /*-----------------------------------------------------------*/
76 
77 /* Objects are allocated and deleted. This structure stores the type
78  * and the size of the object. */
79     typedef struct xTCP_ALLOCATION
80     {
81         TCP_MEMORY_t xMemType;
82         void * pxObject;
83         UBaseType_t uxNumber;
84         size_t uxSize;
85     } TCP_ALLOCATION_t;
86 /*-----------------------------------------------------------*/
87 
88 
89     static void vWriteHeader( void );
90 
91     static size_t uxCurrentMallocSize;
92     static TCP_ALLOCATION_t xAllocations[ ipconfigTCP_MEM_STATS_MAX_ALLOCATION ];
93     static size_t uxAllocationCount;
94     static BaseType_t xFirstItem = pdTRUE;
95     UBaseType_t uxNextObjectNumber;
96     static BaseType_t xCurrentLine = 0;
97     static BaseType_t xFirstDumpLine = 0;
98     static BaseType_t xLastHeaderLineNr = 0;
99     static BaseType_t xLoggingStopped = 0;
100 /*-----------------------------------------------------------*/
101 
vAddAllocation(TCP_MEMORY_t xMemType,void * pxObject,size_t uxSize)102     static void vAddAllocation( TCP_MEMORY_t xMemType,
103                                 void * pxObject,
104                                 size_t uxSize )
105     {
106         size_t uxIndex;
107 
108         vTaskSuspendAll();
109         {
110             for( uxIndex = 0; uxIndex < uxAllocationCount; uxIndex++ )
111             {
112                 if( xAllocations[ uxIndex ].pxObject == pxObject )
113                 {
114                     /* Already added, strange. */
115                     FreeRTOS_printf( ( "vAddAllocation: Pointer %p already added\n", pxObject ) );
116                     break;
117                 }
118             }
119 
120             /* If the object has not been found,
121              * and if there is still space, add the object. */
122             if( ( uxIndex == uxAllocationCount ) &&
123                 ( uxAllocationCount < ipconfigTCP_MEM_STATS_MAX_ALLOCATION ) )
124             {
125                 xAllocations[ uxIndex ].pxObject = pxObject;
126                 xAllocations[ uxIndex ].xMemType = xMemType;
127                 xAllocations[ uxIndex ].uxSize = uxSize;
128                 xAllocations[ uxIndex ].uxNumber = uxNextObjectNumber++;
129                 uxAllocationCount++;
130             }
131         }
132         xTaskResumeAll();
133     }
134 /*-----------------------------------------------------------*/
135 
pxRemoveAllocation(void * pxObject)136     static TCP_ALLOCATION_t * pxRemoveAllocation( void * pxObject )
137     {
138         size_t uxSource = 0, uxTarget = 0;
139         static TCP_ALLOCATION_t xAllocation = { 0 };
140         BaseType_t xFound = pdFALSE;
141         TCP_ALLOCATION_t * pxReturn;
142 
143         vTaskSuspendAll();
144         {
145             for( ; uxSource < uxAllocationCount; uxSource++ )
146             {
147                 if( xAllocations[ uxSource ].pxObject == pxObject )
148                 {
149                     /* This is entry will be removed. */
150                     ( void ) memcpy( &( xAllocation ), &( xAllocations[ uxSource ] ), sizeof xAllocation );
151                     xFound = pdTRUE;
152                 }
153                 else
154                 {
155                     xAllocations[ uxTarget ] = xAllocations[ uxSource ];
156                     uxTarget++;
157                 }
158             }
159 
160             if( xFound != pdFALSE )
161             {
162                 uxAllocationCount--;
163                 pxReturn = &( xAllocation );
164             }
165             else
166             {
167                 pxReturn = NULL;
168             }
169         }
170         xTaskResumeAll();
171         return pxReturn;
172     }
173 /*-----------------------------------------------------------*/
174 
pcTypeName(TCP_MEMORY_t xMemType)175     static const char * pcTypeName( TCP_MEMORY_t xMemType )
176     {
177         switch( xMemType )
178         {
179             case tcpSOCKET_TCP:
180                 return "TCP-Socket";
181 
182             case tcpSOCKET_UDP:
183                 return "UDP-Socket";
184 
185             case tcpSOCKET_SET:
186                 return "SocketSet";
187 
188             case tcpSEMAPHORE:
189                 return "Semaphore";
190 
191             case tcpRX_STREAM_BUFFER:
192                 return "RX-Buffer";
193 
194             case tcpTX_STREAM_BUFFER:
195                 return "TX-Buffer";
196 
197             case tcpNETWORK_BUFFER:
198                 return "networkBuffer";
199         }
200 
201         return "Unknown object";
202     }
203 /*-----------------------------------------------------------*/
204 
vWriteHeader()205     static void vWriteHeader()
206     {
207         size_t uxPacketSize;
208         size_t uxTXSize;
209         size_t uxStaticSize = 0;
210         BaseType_t xFirstLineNr = 0;
211 
212         char pucComment[ 64 ] = "";
213         StreamBuffer_t * pxBuffer = NULL;
214         size_t uxTara = sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray );
215 
216         /* The approximate size of a buffer for a Network Packet. */
217         STATS_PRINTF( ( "TCPMemStat,Some important ipconfig items:\n" ) );
218         STATS_PRINTF( ( "TCPMemStat,ipconfig item,Value,Comment\n" ) );
219         STATS_PRINTF( ( "TCPMemStat,NETWORK_MTU,%u\n", ipconfigNETWORK_MTU ) );
220         STATS_PRINTF( ( "TCPMemStat,TCP_MSS,%u\n", ipconfigTCP_MSS ) );
221         STATS_PRINTF( ( "TCPMemStat,USE_TCP,%u\n", ipconfigUSE_TCP ) );
222         STATS_PRINTF( ( "TCPMemStat,USE_TCP_WIN,%u\n", ipconfigUSE_TCP_WIN ) );
223 
224         uxTXSize = ( size_t ) FreeRTOS_round_up( ipconfigTCP_TX_BUFFER_LENGTH, ipconfigTCP_MSS );
225 
226         STATS_PRINTF( ( "TCPMemStat,TCP_RX_BUFFER_LENGTH,%u,Plus %u bytes\n", ipconfigTCP_RX_BUFFER_LENGTH, uxTara + STREAM_BUFFER_ROUNDUP_BYTES ) );
227 
228         if( uxTXSize > ipconfigTCP_TX_BUFFER_LENGTH )
229         {
230             snprintf( pucComment, sizeof pucComment, ",Rounded up to %u x MSS (plus %u bytes)", uxTXSize / ipconfigTCP_MSS, uxTara + STREAM_BUFFER_ROUNDUP_BYTES );
231         }
232 
233         STATS_PRINTF( ( "TCPMemStat,TCP_TX_BUFFER_LENGTH,%u%s\n", ipconfigTCP_TX_BUFFER_LENGTH, pucComment ) );
234         STATS_PRINTF( ( "TCPMemStat,USE_DHCP,%u\n", ipconfigUSE_DHCP ) );
235 
236         /*
237          * Start of fixed RAM allocations.
238          */
239 
240         STATS_PRINTF( ( "TCPMemStat,\n" ) );
241         STATS_PRINTF( ( "TCPMemStat,RAM that is always allocated either statically or on the heap:\n" ) );
242         STATS_PRINTF( ( "TCPMemStat,ipconfig item,Value,PerUnit,Total\n" ) );
243         xFirstLineNr = xCurrentLine;
244 
245         if( xBufferAllocFixedSize != 0 )
246         {
247             size_t uxBytes;
248 
249             /* Using BufferAllocation_1.c */
250             uxPacketSize = ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 31 ) & ~0x1FuL;
251             uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * ( uxPacketSize + sizeof( NetworkBufferDescriptor_t ) );
252 
253             STATS_PRINTF( ( "TCPMemStat,NUM_NETWORK_BUFFER_DESCRIPTORS,%u,%u,=B%d*C%d,Descriptors + buffers\n",
254                             ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS,
255                             uxPacketSize + sizeof( NetworkBufferDescriptor_t ),
256                             xCurrentLine,
257                             xCurrentLine ) );
258             uxStaticSize += uxBytes;
259         }
260         else
261         {
262             size_t uxBytes;
263 
264             /* Using BufferAllocation_2.c */
265             uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * sizeof( NetworkBufferDescriptor_t );
266             STATS_PRINTF( ( "TCPMemStat,NUM_NETWORK_BUFFER_DESCRIPTORS,%u,%u,=B%d*C%d,Descriptors only\n",
267                             ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS,
268                             sizeof( NetworkBufferDescriptor_t ),
269                             xCurrentLine,
270                             xCurrentLine ) );
271             uxStaticSize += uxBytes;
272         }
273 
274         {
275             #if ( ipconfigUSE_TCP_WIN != 0 )
276                 {
277                     STATS_PRINTF( ( "TCPMemStat,TCP_WIN_SEG_COUNT,%u,%u,=B%d*C%d\n",
278                                     ipconfigTCP_WIN_SEG_COUNT, sizeof( TCPSegment_t ), xCurrentLine, xCurrentLine ) );
279                 }
280             #else
281                 {
282                     STATS_PRINTF( ( "TCPMemStat,TCP_WIN_SEG_COUNT,%u,%u\n", 0, 0 ) );
283                 }
284             #endif
285         }
286         {
287             size_t uxBytes;
288             size_t uxEntrySize;
289 
290             uxBytes = ipconfigEVENT_QUEUE_LENGTH * sizeof( IPStackEvent_t );
291             STATS_PRINTF( ( "TCPMemStat,EVENT_QUEUE_LENGTH,%u,%u,=B%d*C%d\n",
292                             ipconfigEVENT_QUEUE_LENGTH,
293                             sizeof( IPStackEvent_t ),
294                             xCurrentLine,
295                             xCurrentLine ) );
296             uxStaticSize += uxBytes;
297 
298             uxBytes = ipconfigIP_TASK_STACK_SIZE_WORDS * sizeof( void * );
299             STATS_PRINTF( ( "TCPMemStat,IP_TASK_STACK_SIZE_WORDS,%u,%u,=B%d*C%d\n",
300                             ipconfigIP_TASK_STACK_SIZE_WORDS,
301                             sizeof( void * ),
302                             xCurrentLine,
303                             xCurrentLine ) );
304             uxStaticSize += uxBytes;
305 
306             uxBytes = ipconfigARP_CACHE_ENTRIES * sizeof( ARPCacheRow_t );
307             STATS_PRINTF( ( "TCPMemStat,ARP_CACHE_ENTRIES,%u,%u,=B%d*C%d\n",
308                             ipconfigARP_CACHE_ENTRIES,
309                             sizeof( ARPCacheRow_t ),
310                             xCurrentLine,
311                             xCurrentLine ) );
312             uxStaticSize += uxBytes;
313 
314             #if ( ipconfigUSE_DNS_CACHE == 1 )
315                 {
316                     uxEntrySize = 3u * sizeof( uint32_t ) + ( ( ipconfigDNS_CACHE_NAME_LENGTH + 3 ) & ~0x3u );
317                     STATS_PRINTF( ( "TCPMemStat,DNS_CACHE_ENTRIES,%u,%u,=B%d*C%d\n",
318                                     ipconfigDNS_CACHE_ENTRIES,
319                                     uxEntrySize,
320                                     xCurrentLine,
321                                     xCurrentLine ) );
322                 }
323             #endif
324         }
325 
326         /*
327          * End of fixed RAM allocations.
328          */
329         if( xBufferAllocFixedSize != 0 )
330         {
331             pucComment[ 0 ] = 0;
332         }
333         else
334         {
335             size_t uxBytes;
336 
337             /* BufferAllocation_2.c uses HEAP to store network packets. */
338             uxPacketSize = ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER + ipBUFFER_PADDING + 3 ) & ~0x03uL;
339             uxBytes = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * uxPacketSize;
340             STATS_PRINTF( ( "TCPMemStat,Network buffers in HEAP,%u,%u,=B%d*C%d\n",
341                             ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS,
342                             uxPacketSize,
343                             xCurrentLine,
344                             xCurrentLine ) );
345             uxStaticSize += uxBytes;
346             snprintf( pucComment, sizeof pucComment, "Actual size fluctuates because BufferAllocation_2.c is used" );
347         }
348 
349         xLastHeaderLineNr = xCurrentLine;
350 
351         STATS_PRINTF( ( "TCPMemStat,Total,,,=SUM(D%d:D%d),%s\n", xFirstLineNr + 1, xLastHeaderLineNr, pucComment ) );
352 
353         STATS_PRINTF( ( "TCPMemStat,\n" ) );
354         STATS_PRINTF( ( "TCPMemStat,\n" ) );
355         STATS_PRINTF( ( "TCPMemStat,The following allocations are done on the heap while running:\n" ) );
356         STATS_PRINTF( ( "TCPMemStat,Create/Remove,Object,Size,Heap use,Pointer,Heap-min,Heap-Cur,comment\n" ) );
357     }
358 /*-----------------------------------------------------------*/
359 
vTCPMemStatCreate(TCP_MEMORY_t xMemType,void * pxObject,size_t uxSize)360     void vTCPMemStatCreate( TCP_MEMORY_t xMemType,
361                             void * pxObject,
362                             size_t uxSize )
363     {
364         if( xLoggingStopped == pdFALSE )
365         {
366             StreamBuffer_t * pxBuffer = NULL;
367             char pcExtra[ 81 ] = "";
368 
369             if( xFirstItem != pdFALSE )
370             {
371                 xFirstItem = pdFALSE;
372                 vWriteHeader();
373             }
374 
375             if( ( xMemType == tcpRX_STREAM_BUFFER ) || ( xMemType == tcpTX_STREAM_BUFFER ) )
376             {
377                 size_t uxTara = sizeof( *pxBuffer ) - sizeof( pxBuffer->ucArray );
378                 size_t uxNett = uxSize - uxTara;
379 
380                 snprintf( pcExtra, sizeof pcExtra, ",%u nett", uxNett );
381             }
382 
383             if( xFirstDumpLine == 0 )
384             {
385                 xFirstDumpLine = xCurrentLine + 1;
386             }
387 
388             xCurrentLine++;
389             configPRINTF( ( "TCPMemStat,CREATE,%s,%lu,%lu,%u,%u,%u%s\n",
390                             pcTypeName( xMemType ),
391                             uxSize,
392                             uxCurrentMallocSize + uxSize,
393                             uxNextObjectNumber,
394                             xPortGetMinimumEverFreeHeapSize(),
395                             xPortGetFreeHeapSize(),
396                             pcExtra ) );
397             uxCurrentMallocSize += uxSize;
398             vAddAllocation( xMemType, pxObject, uxSize );
399         }
400     }
401 /*-----------------------------------------------------------*/
402 
vTCPMemStatDelete(void * pxObject)403     void vTCPMemStatDelete( void * pxObject )
404     {
405         if( xLoggingStopped == pdFALSE )
406         {
407             TCP_ALLOCATION_t * pxFound = pxRemoveAllocation( pxObject );
408 
409             if( xFirstDumpLine == 0 )
410             {
411                 xFirstDumpLine = xCurrentLine + 1;
412             }
413 
414             if( pxFound == NULL )
415             {
416                 FreeRTOS_printf( ( "TCPMemStat: can not find pointer %p\n", pxObject ) );
417             }
418             else
419             {
420                 xCurrentLine++;
421                 configPRINTF( ( "TCPMemStat,REMOVE,%s,-%lu,%lu,%x,%u,%u\n",
422                                 pcTypeName( pxFound->xMemType ),
423                                 pxFound->uxSize,
424                                 uxCurrentMallocSize - pxFound->uxSize,
425                                 pxFound->uxNumber,
426                                 xPortGetMinimumEverFreeHeapSize(),
427                                 xPortGetFreeHeapSize() ) );
428 
429                 if( uxCurrentMallocSize < pxFound->uxSize )
430                 {
431                     uxCurrentMallocSize = 0uL;
432                 }
433                 else
434                 {
435                     uxCurrentMallocSize -= pxFound->uxSize;
436                 }
437             }
438         }
439     }
440 /*-----------------------------------------------------------*/
441 
vTCPMemStatClose()442     void vTCPMemStatClose()
443     {
444         if( xLoggingStopped == pdFALSE )
445         {
446             /* name;object;size;Heap;Ppointer;HeapMin;HeapDur;Comment */
447             BaseType_t xLastLineNr = xCurrentLine;
448 
449             xLoggingStopped = pdTRUE;
450 
451             STATS_PRINTF( ( "TCPMemStat,Totals,,,=MAX(D%d:D%d),,=MIN(F%d:F%d),=MAX(G%d:G%d)\n",
452                             xFirstDumpLine,
453                             xLastLineNr,
454                             xFirstDumpLine,
455                             xLastLineNr,
456                             xFirstDumpLine,
457                             xLastLineNr ) );
458             STATS_PRINTF( ( "TCPMemStat,Maximum RAM usage:,,,=SUM(D%d;D%d)\n",
459                             xLastHeaderLineNr + 1,
460                             xLastLineNr + 1 ) );
461         }
462     }
463 /*-----------------------------------------------------------*/
464 
465 #endif /* ( ipconfigUSE_TCP_MEM_STATS != 0 ) */
466