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