diff options
Diffstat (limited to 'FreeRTOS-Plus/Test/FreeRTOS-Plus-TCP/Integration/Full-TCP-Suite/Logging/demo_logging.c')
-rw-r--r-- | FreeRTOS-Plus/Test/FreeRTOS-Plus-TCP/Integration/Full-TCP-Suite/Logging/demo_logging.c | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/FreeRTOS-Plus/Test/FreeRTOS-Plus-TCP/Integration/Full-TCP-Suite/Logging/demo_logging.c b/FreeRTOS-Plus/Test/FreeRTOS-Plus-TCP/Integration/Full-TCP-Suite/Logging/demo_logging.c new file mode 100644 index 000000000..0d33b0968 --- /dev/null +++ b/FreeRTOS-Plus/Test/FreeRTOS-Plus-TCP/Integration/Full-TCP-Suite/Logging/demo_logging.c @@ -0,0 +1,526 @@ +/* + * FreeRTOS Kernel V10.3.0 + * 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://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * Logging utility that allows FreeRTOS tasks to log to a UDP port, stdout, and + * disk file without making any Win32 system calls themselves. + * + * Messages logged to a UDP port are sent directly (using FreeRTOS+TCP), but as + * FreeRTOS tasks cannot make Win32 system calls messages sent to stdout or a + * disk file are sent via a stream buffer to a Win32 thread which then performs + * the actual output. + */ + +/* Standard includes. */ +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <io.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" + +/* Demo includes. */ +#include "demo_logging.h" + +/*-----------------------------------------------------------*/ + +/* The maximum size to which the log file may grow, before being renamed +to .ful. */ +#define dlLOGGING_FILE_SIZE ( 40ul * 1024ul * 1024ul ) + +/* Dimensions the arrays into which print messages are created. */ +#define dlMAX_PRINT_STRING_LENGTH 255 + +/* The size of the stream buffer used to pass messages from FreeRTOS tasks to +the Win32 thread that is responsible for making any Win32 system calls that are +necessary for the selected logging method. */ +#define dlLOGGING_STREAM_BUFFER_SIZE 32768 + +/* A block time of zero simply means don't block. */ +#define dlDONT_BLOCK 0 + +/*-----------------------------------------------------------*/ + +/* + * Called from vLoggingInit() to start a new disk log file. + */ +static void prvFileLoggingInit( void ); + +/* + * Attempt to write a message to the file. + */ +static void prvLogToFile( const char *pcMessage, size_t xLength ); + +/* + * Simply close the logging file, if it is open. + */ +static void prvFileClose( void ); + +/* + * Before the scheduler is started this function is called directly. After the + * scheduler has started it is called from the Windows thread dedicated to + * outputting log messages. Only the windows thread actually performs the + * writing so as not to disrupt the simulation by making Windows system calls + * from FreeRTOS tasks. + */ +static void prvLoggingFlushBuffer( void ); + +/* + * The windows thread that performs the actual writing of messages that require + * Win32 system calls. Only the windows thread can make system calls so as not + * to disrupt the simulation by making Windows calls from FreeRTOS tasks. + */ +static DWORD WINAPI prvWin32LoggingThread( void *pvParam ); + +/* + * Creates the socket to which UDP messages are sent. This function is not + * called directly to prevent the print socket being created from within the IP + * task - which could result in a deadlock. Instead the function call is + * deferred to run in the RTOS daemon task - hence it prototype. + */ +static void prvCreatePrintSocket( void *pvParameter1, uint32_t ulParameter2 ); + +/*-----------------------------------------------------------*/ + +/* Windows event used to wake the Win32 thread which performs any logging that +needs Win32 system calls. */ +static void *pvLoggingThreadEvent = NULL; + +/* Stores the selected logging targets passed in as parameters to the +vLoggingInit() function. */ +BaseType_t xStdoutLoggingUsed = pdFALSE, xDiskFileLoggingUsed = pdFALSE, xUDPLoggingUsed = pdFALSE; + +/* Circular buffer used to pass messages from the FreeRTOS tasks to the Win32 +thread that is responsible for making Win32 calls (when stdout or a disk log is +used). */ +static StreamBuffer_t *xLogStreamBuffer = NULL; + +/* Handle to the file used for logging. This is left open while there are +messages waiting to be logged, then closed again in between logs. */ +static FILE *pxLoggingFileHandle = NULL; + +/* When true prints are performed directly. After start up xDirectPrint is set +to pdFALSE - at which time prints that require Win32 system calls are done by +the Win32 thread responsible for logging. */ +BaseType_t xDirectPrint = pdTRUE; + +/* File names for the in use and complete (full) log files. */ +static const char *pcLogFileName = "RTOSDemo.log"; +static const char *pcFullLogFileName = "RTOSDemo.ful"; + +/* Keep the current file size in a variable, as an optimisation. */ +static size_t ulSizeOfLoggingFile = 0ul; + +/* The UDP socket and address on/to which print messages are sent. */ +Socket_t xPrintSocket = FREERTOS_INVALID_SOCKET; +struct freertos_sockaddr xPrintUDPAddress; + +/*-----------------------------------------------------------*/ + +void vLoggingInit( BaseType_t xLogToStdout, BaseType_t xLogToFile, BaseType_t xLogToUDP, uint32_t ulRemoteIPAddress, uint16_t usRemotePort ) +{ + /* Can only be called before the scheduler has started. */ + configASSERT( xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED ); + + #if( ( ipconfigHAS_DEBUG_PRINTF == 1 ) || ( ipconfigHAS_PRINTF == 1 ) ) + { + HANDLE Win32Thread; + + /* Record which output methods are to be used. */ + xStdoutLoggingUsed = xLogToStdout; + xDiskFileLoggingUsed = xLogToFile; + xUDPLoggingUsed = xLogToUDP; + + /* If a disk file is used then initialise it now. */ + if( xDiskFileLoggingUsed != pdFALSE ) + { + prvFileLoggingInit(); + } + + /* If UDP logging is used then store the address to which the log data + will be sent - but don't create the socket yet because the network is + not initialised. */ + if( xUDPLoggingUsed != pdFALSE ) + { + /* Set the address to which the print messages are sent. */ + xPrintUDPAddress.sin_port = FreeRTOS_htons( usRemotePort ); + xPrintUDPAddress.sin_addr = ulRemoteIPAddress; + } + + /* If a disk file or stdout are to be used then Win32 system calls will + have to be made. Such system calls cannot be made from FreeRTOS tasks + so create a stream buffer to pass the messages to a Win32 thread, then + create the thread itself, along with a Win32 event that can be used to + unblock the thread. */ + if( ( xStdoutLoggingUsed != pdFALSE ) || ( xDiskFileLoggingUsed != pdFALSE ) ) + { + /* Create the buffer. */ + xLogStreamBuffer = ( StreamBuffer_t * ) malloc( sizeof( *xLogStreamBuffer ) - sizeof( xLogStreamBuffer->ucArray ) + dlLOGGING_STREAM_BUFFER_SIZE + 1 ); + configASSERT( xLogStreamBuffer ); + memset( xLogStreamBuffer, '\0', sizeof( *xLogStreamBuffer ) - sizeof( xLogStreamBuffer->ucArray ) ); + xLogStreamBuffer->LENGTH = dlLOGGING_STREAM_BUFFER_SIZE + 1; + + /* Create the Windows event. */ + pvLoggingThreadEvent = CreateEvent( NULL, FALSE, TRUE, "StdoutLoggingEvent" ); + + /* Create the thread itself. */ + Win32Thread = CreateThread( + NULL, /* Pointer to thread security attributes. */ + 0, /* Initial thread stack size, in bytes. */ + prvWin32LoggingThread, /* Pointer to thread function. */ + NULL, /* Argument for new thread. */ + 0, /* Creation flags. */ + NULL ); + + /* Use the cores that are not used by the FreeRTOS tasks. */ + SetThreadAffinityMask( Win32Thread, ~0x01u ); + SetThreadPriorityBoost( Win32Thread, TRUE ); + SetThreadPriority( Win32Thread, THREAD_PRIORITY_IDLE ); + } + } + #else + { + /* FreeRTOSIPConfig is set such that no print messages will be output. + Avoid compiler warnings about unused parameters. */ + ( void ) xLogToStdout; + ( void ) xLogToFile; + ( void ) xLogToUDP; + ( void ) usRemotePort; + ( void ) ulRemoteIPAddress; + } + #endif /* ( ipconfigHAS_DEBUG_PRINTF == 1 ) || ( ipconfigHAS_PRINTF == 1 ) */ +} +/*-----------------------------------------------------------*/ + +static void prvCreatePrintSocket( void *pvParameter1, uint32_t ulParameter2 ) +{ +static const TickType_t xSendTimeOut = pdMS_TO_TICKS( 0 ); +Socket_t xSocket; + + /* The function prototype is that of a deferred function, but the parameters + are not actually used. */ + ( void ) pvParameter1; + ( void ) ulParameter2; + + xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); + + if( xSocket != FREERTOS_INVALID_SOCKET ) + { + /* FreeRTOS+TCP decides which port to bind to. */ + FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, &xSendTimeOut, sizeof( xSendTimeOut ) ); + FreeRTOS_bind( xSocket, NULL, 0 ); + + /* Now the socket is bound it can be assigned to the print socket. */ + xPrintSocket = xSocket; + } +} +/*-----------------------------------------------------------*/ + +void vLoggingPrintf( const char *pcFormat, ... ) +{ +char cPrintString[ dlMAX_PRINT_STRING_LENGTH ]; +char cOutputString[ dlMAX_PRINT_STRING_LENGTH ]; +char *pcSource, *pcTarget, *pcBegin; +size_t xLength, xLength2, rc; +static BaseType_t xMessageNumber = 0; +va_list args; +uint32_t ulIPAddress; +const char *pcTaskName; +const char *pcNoTask = "None"; +int iOriginalPriority; +HANDLE xCurrentTask; + + + if( ( xStdoutLoggingUsed != pdFALSE ) || ( xDiskFileLoggingUsed != pdFALSE ) || ( xUDPLoggingUsed != pdFALSE ) ) + { + /* There are a variable number of parameters. */ + va_start( args, pcFormat ); + + /* Additional info to place at the start of the log. */ + if( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED ) + { + pcTaskName = pcTaskGetName( NULL ); + } + else + { + pcTaskName = pcNoTask; + } + + if( strcmp( pcFormat, "\n" ) != 0 ) + { + xLength = snprintf( cPrintString, dlMAX_PRINT_STRING_LENGTH, "%lu %lu [%s] ", + xMessageNumber++, + ( unsigned long ) xTaskGetTickCount(), + pcTaskName ); + } + else + { + xLength = 0; + memset( cPrintString, 0x00, dlMAX_PRINT_STRING_LENGTH ); + } + + xLength2 = vsnprintf( cPrintString + xLength, dlMAX_PRINT_STRING_LENGTH - xLength, pcFormat, args ); + + if( xLength2 < 0 ) + { + /* Clean up. */ + xLength2 = dlMAX_PRINT_STRING_LENGTH - 1 - xLength; + cPrintString[ dlMAX_PRINT_STRING_LENGTH - 1 ] = '\0'; + } + + xLength += xLength2; + va_end( args ); + + /* For ease of viewing, copy the string into another buffer, converting + IP addresses to dot notation on the way. */ + pcSource = cPrintString; + pcTarget = cOutputString; + + while( ( *pcSource ) != '\0' ) + { + *pcTarget = *pcSource; + pcTarget++; + pcSource++; + + /* Look forward for an IP address denoted by 'ip'. */ + if( ( isxdigit( pcSource[ 0 ] ) != pdFALSE ) && ( pcSource[ 1 ] == 'i' ) && ( pcSource[ 2 ] == 'p' ) ) + { + *pcTarget = *pcSource; + pcTarget++; + *pcTarget = '\0'; + pcBegin = pcTarget - 8; + + while( ( pcTarget > pcBegin ) && ( isxdigit( pcTarget[ -1 ] ) != pdFALSE ) ) + { + pcTarget--; + } + + sscanf( pcTarget, "%8X", &ulIPAddress ); + rc = sprintf( pcTarget, "%lu.%lu.%lu.%lu", + ( unsigned long ) ( ulIPAddress >> 24UL ), + ( unsigned long ) ( (ulIPAddress >> 16UL) & 0xffUL ), + ( unsigned long ) ( (ulIPAddress >> 8UL) & 0xffUL ), + ( unsigned long ) ( ulIPAddress & 0xffUL ) ); + pcTarget += rc; + pcSource += 3; /* skip "<n>ip" */ + } + } + + /* How far through the buffer was written? */ + xLength = ( BaseType_t ) ( pcTarget - cOutputString ); + + /* If the message is to be logged to a UDP port then it can be sent directly + because it only uses FreeRTOS function (not Win32 functions). */ + if( xUDPLoggingUsed != pdFALSE ) + { + if( ( xPrintSocket == FREERTOS_INVALID_SOCKET ) && ( FreeRTOS_IsNetworkUp() != pdFALSE ) ) + { + /* Create and bind the socket to which print messages are sent. The + xTimerPendFunctionCall() function is used even though this is + not an interrupt because this function is called from the IP task + and the IP task cannot itself wait for a socket to bind. The + parameters to prvCreatePrintSocket() are not required so set to + NULL or 0. */ + xTimerPendFunctionCall( prvCreatePrintSocket, NULL, 0, dlDONT_BLOCK ); + } + + if( xPrintSocket != FREERTOS_INVALID_SOCKET ) + { + FreeRTOS_sendto( xPrintSocket, cOutputString, xLength, 0, &xPrintUDPAddress, sizeof( xPrintUDPAddress ) ); + + /* Just because the UDP data logger I'm using is dumb. */ + FreeRTOS_sendto( xPrintSocket, "\r", sizeof( char ), 0, &xPrintUDPAddress, sizeof( xPrintUDPAddress ) ); + } + } + + /* If logging is also to go to either stdout or a disk file then it cannot + be output here - so instead write the message to the stream buffer and wake + the Win32 thread which will read it from the stream buffer and perform the + actual output. */ + if( ( xStdoutLoggingUsed != pdFALSE ) || ( xDiskFileLoggingUsed != pdFALSE ) ) + { + configASSERT( xLogStreamBuffer ); + + /* How much space is in the buffer? */ + xLength2 = uxStreamBufferGetSpace( xLogStreamBuffer ); + + /* There must be enough space to write both the string and the length of + the string. */ + if( xLength2 >= ( xLength + sizeof( xLength ) ) ) + { + /* First write in the length of the data, then write in the data + itself. Raising the thread priority is used as a critical section + as there are potentially multiple writers. The stream buffer is + only thread safe when there is a single writer (likewise for + reading from the buffer). */ + xCurrentTask = GetCurrentThread(); + iOriginalPriority = GetThreadPriority( xCurrentTask ); + SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ); + uxStreamBufferAdd( xLogStreamBuffer, 0, ( const uint8_t * ) &( xLength ), sizeof( xLength ) ); + uxStreamBufferAdd( xLogStreamBuffer, 0, ( const uint8_t * ) cOutputString, xLength ); + SetThreadPriority( GetCurrentThread(), iOriginalPriority ); + } + + /* xDirectPrint is initialised to pdTRUE, and while it remains true the + logging output function is called directly. When the system is running + the output function cannot be called directly because it would get + called from both FreeRTOS tasks and Win32 threads - so instead wake the + Win32 thread responsible for the actual output. */ + if( xDirectPrint != pdFALSE ) + { + /* While starting up, the thread which calls prvWin32LoggingThread() + is not running yet and xDirectPrint will be pdTRUE. */ + prvLoggingFlushBuffer(); + } + else if( pvLoggingThreadEvent != NULL ) + { + /* While running, wake up prvWin32LoggingThread() to send the + logging data. */ + SetEvent( pvLoggingThreadEvent ); + } + } + } +} +/*-----------------------------------------------------------*/ + +static void prvLoggingFlushBuffer( void ) +{ +size_t xLength; +char cPrintString[ dlMAX_PRINT_STRING_LENGTH ]; + + /* Is there more than the length value stored in the circular buffer + used to pass data from the FreeRTOS simulator into this Win32 thread? */ + while( uxStreamBufferGetSize( xLogStreamBuffer ) > sizeof( xLength ) ) + { + memset( cPrintString, 0x00, dlMAX_PRINT_STRING_LENGTH ); + uxStreamBufferGet( xLogStreamBuffer, 0, ( uint8_t * ) &xLength, sizeof( xLength ), pdFALSE ); + uxStreamBufferGet( xLogStreamBuffer, 0, ( uint8_t * ) cPrintString, xLength, pdFALSE ); + + /* Write the message to standard out if requested to do so when + vLoggingInit() was called, or if the network is not yet up. */ + if( ( xStdoutLoggingUsed != pdFALSE ) || ( FreeRTOS_IsNetworkUp() == pdFALSE ) ) + { + /* Write the message to stdout. */ + printf( "%s", cPrintString ); /*_RB_ Replace with _write(). */ + } + + /* Write the message to a file if requested to do so when + vLoggingInit() was called. */ + if( xDiskFileLoggingUsed != pdFALSE ) + { + prvLogToFile( cPrintString, xLength ); + } + } + + prvFileClose(); +} +/*-----------------------------------------------------------*/ + +static DWORD WINAPI prvWin32LoggingThread( void *pvParameter ) +{ +const DWORD xMaxWait = 1000; + + ( void ) pvParameter; + + /* From now on, prvLoggingFlushBuffer() will only be called from this + Windows thread */ + xDirectPrint = pdFALSE; + + for( ;; ) + { + /* Wait to be told there are message waiting to be logged. */ + WaitForSingleObject( pvLoggingThreadEvent, xMaxWait ); + + /* Write out all waiting messages. */ + prvLoggingFlushBuffer(); + } +} +/*-----------------------------------------------------------*/ + +static void prvFileLoggingInit( void ) +{ +FILE *pxHandle = fopen( pcLogFileName, "a" ); + + if( pxHandle != NULL ) + { + fseek( pxHandle, SEEK_END, 0ul ); + ulSizeOfLoggingFile = ftell( pxHandle ); + fclose( pxHandle ); + } + else + { + ulSizeOfLoggingFile = 0ul; + } +} +/*-----------------------------------------------------------*/ + +static void prvFileClose( void ) +{ + if( pxLoggingFileHandle != NULL ) + { + fclose( pxLoggingFileHandle ); + pxLoggingFileHandle = NULL; + } +} +/*-----------------------------------------------------------*/ + +static void prvLogToFile( const char *pcMessage, size_t xLength ) +{ + if( pxLoggingFileHandle == NULL ) + { + pxLoggingFileHandle = fopen( pcLogFileName, "a" ); + } + + if( pxLoggingFileHandle != NULL ) + { + fwrite( pcMessage, 1, xLength, pxLoggingFileHandle ); + ulSizeOfLoggingFile += xLength; + + /* If the file has grown to its maximum permissible size then close and + rename it - then start with a new file. */ + if( ulSizeOfLoggingFile > ( size_t ) dlLOGGING_FILE_SIZE ) + { + prvFileClose(); + if( _access( pcFullLogFileName, 00 ) == 0 ) + { + remove( pcFullLogFileName ); + } + rename( pcLogFileName, pcFullLogFileName ); + ulSizeOfLoggingFile = 0; + } + } +} +/*-----------------------------------------------------------*/ + |