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