/* * FreeRTOS Common V1.1.3 * Copyright (C) 2020 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://aws.amazon.com/freertos * http://www.FreeRTOS.org */ /** * @file iot_logging.c * @brief Implementation of logging functions from iot_logging.h */ /* The config header is always included first. */ #include "iot_config.h" /* Standard includes. */ #include <stdarg.h> #include <stdio.h> #include <string.h> /* Platform clock include. */ #include "platform/iot_clock.h" /* Logging includes. */ #include "private/iot_logging.h" /*-----------------------------------------------------------*/ /* This implementation assumes the following values for the log level constants. * Ensure that the values have not been modified. */ #if IOT_LOG_NONE != 0 #error "IOT_LOG_NONE must be 0." #endif #if IOT_LOG_ERROR != 1 #error "IOT_LOG_ERROR must be 1." #endif #if IOT_LOG_WARN != 2 #error "IOT_LOG_WARN must be 2." #endif #if IOT_LOG_INFO != 3 #error "IOT_LOG_INFO must be 3." #endif #if IOT_LOG_DEBUG != 4 #error "IOT_LOG_DEBUG must be 4." #endif /** * @def IotLogging_Puts( message ) * @brief Function the logging library uses to print a line. * * This function can be set by using a define. By default, the standard library * [puts](http://pubs.opengroup.org/onlinepubs/9699919799/functions/puts.html) * function is used. */ #ifndef IotLogging_Puts #define IotLogging_Puts puts #endif /* * Provide default values for undefined memory allocation functions based on * the usage of dynamic memory allocation. */ #if IOT_STATIC_MEMORY_ONLY == 1 /* Static memory allocation header. */ #include "private/iot_static_memory.h" /** * @brief Allocate a new logging buffer. This function must have the same * signature as [malloc](http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html). */ #ifndef IotLogging_Malloc #define IotLogging_Malloc Iot_MallocMessageBuffer #endif /** * @brief Free a logging buffer. This function must have the same signature * as [free](http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html). */ #ifndef IotLogging_Free #define IotLogging_Free Iot_FreeMessageBuffer #endif /** * @brief Get the size of a logging buffer. Statically-allocated buffers * should all have the same size. */ #ifndef IotLogging_StaticBufferSize #define IotLogging_StaticBufferSize Iot_MessageBufferSize #endif #else /* if IOT_STATIC_MEMORY_ONLY == 1 */ #ifndef IotLogging_Malloc #include <stdlib.h> #define IotLogging_Malloc malloc #endif #ifndef IotLogging_Free #include <stdlib.h> #define IotLogging_Free free #endif #endif /* if IOT_STATIC_MEMORY_ONLY == 1 */ /** * @brief A guess of the maximum length of a timestring. * * There's no way for this logging library to know the length of a timestring * before it's generated. Therefore, the logging library will assume a maximum * length of any timestring it may get. This value should be generous enough * to accommodate the vast majority of timestrings. * * @see IotClock_GetTimestring */ #define MAX_TIMESTRING_LENGTH ( 64 ) /** * @brief The longest string in #_pLogLevelStrings (below), plus 3 to accommodate * `[]` and a null-terminator. */ #define MAX_LOG_LEVEL_LENGTH ( 8 ) /** * @brief How many bytes @ref logging_function_genericprintbuffer should output on * each line. */ #define BYTES_PER_LINE ( 16 ) /*-----------------------------------------------------------*/ /** * @brief Lookup table for log levels. * * Converts one of the @ref logging_constants_levels to a string. */ static const char * const _pLogLevelStrings[ 5 ] = { "", /* IOT_LOG_NONE */ "ERROR", /* IOT_LOG_ERROR */ "WARN ", /* IOT_LOG_WARN */ "INFO ", /* IOT_LOG_INFO */ "DEBUG" /* IOT_LOG_DEBUG */ }; /*-----------------------------------------------------------*/ #if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 ) static bool _reallocLoggingBuffer( void ** pOldBuffer, size_t newSize, size_t oldSize ) { bool status = false; /* Allocate a new, larger buffer. */ void * pNewBuffer = IotLogging_Malloc( newSize ); /* Ensure that memory allocation succeeded. */ if( pNewBuffer != NULL ) { /* Copy the data from the old buffer to the new buffer. */ ( void ) memcpy( pNewBuffer, *pOldBuffer, oldSize ); /* Free the old buffer and update the pointer. */ IotLogging_Free( *pOldBuffer ); *pOldBuffer = pNewBuffer; status = true; } return status; } #endif /* if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 ) */ /*-----------------------------------------------------------*/ void IotLog_Generic( int libraryLogSetting, const char * const pLibraryName, int messageLevel, const IotLogConfig_t * const pLogConfig, const char * const pFormat, ... ) { int requiredMessageSize = 0; size_t bufferSize = 0, bufferPosition = 0, timestringLength = 0; char * pLoggingBuffer = NULL; va_list args; /* If the library's log level setting is lower than the message level, * return without doing anything. */ if( ( messageLevel == 0 ) || ( messageLevel > libraryLogSetting ) ) { return; } if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) ) { /* Add length of log level if requested. */ bufferSize += MAX_LOG_LEVEL_LENGTH; } /* Estimate the amount of buffer needed for this log message. */ if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) ) { /* Add size of library name if requested. Add 2 to accommodate "[]". */ bufferSize += strlen( pLibraryName ) + 2; } if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) ) { /* Add length of timestring if requested. */ bufferSize += MAX_TIMESTRING_LENGTH; } /* Add 64 as an initial (arbitrary) guess for the length of the message. */ bufferSize += 64; /* In static memory mode, check that the log message will fit in the a * static buffer. */ #if IOT_STATIC_MEMORY_ONLY == 1 if( bufferSize >= IotLogging_StaticBufferSize() ) { /* If the static buffers are likely too small to fit the log message, * return. */ return; } /* Otherwise, update the buffer size to the size of a static buffer. */ bufferSize = IotLogging_StaticBufferSize(); #endif /* Allocate memory for the logging buffer. */ pLoggingBuffer = ( char * ) IotLogging_Malloc( bufferSize ); if( pLoggingBuffer == NULL ) { return; } /* Print the message log level if requested. */ if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) ) { /* Ensure that message level is valid. */ if( ( messageLevel >= IOT_LOG_NONE ) && ( messageLevel <= IOT_LOG_DEBUG ) ) { /* Add the log level string to the logging buffer. */ requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition, bufferSize - bufferPosition, "[%s]", _pLogLevelStrings[ messageLevel ] ); /* Check for encoding errors. */ if( requiredMessageSize <= 0 ) { IotLogging_Free( pLoggingBuffer ); return; } /* Update the buffer position. */ bufferPosition += ( size_t ) requiredMessageSize; } } /* Print the library name if requested. */ if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) ) { /* Add the library name to the logging buffer. */ requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition, bufferSize - bufferPosition, "[%s]", pLibraryName ); /* Check for encoding errors. */ if( requiredMessageSize <= 0 ) { IotLogging_Free( pLoggingBuffer ); return; } /* Update the buffer position. */ bufferPosition += ( size_t ) requiredMessageSize; } /* Print the timestring if requested. */ if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) ) { /* Add the opening '[' enclosing the timestring. */ pLoggingBuffer[ bufferPosition ] = '['; bufferPosition++; /* Generate the timestring and add it to the buffer. */ if( IotClock_GetTimestring( pLoggingBuffer + bufferPosition, bufferSize - bufferPosition, ×tringLength ) == true ) { /* If the timestring was successfully generated, add the closing "]". */ bufferPosition += timestringLength; pLoggingBuffer[ bufferPosition ] = ']'; bufferPosition++; } else { /* Sufficient memory for a timestring should have been allocated. A timestring * probably failed to generate due to a clock read error; remove the opening '[' * from the logging buffer. */ bufferPosition--; pLoggingBuffer[ bufferPosition ] = '\0'; } } /* Add a padding space between the last closing ']' and the message, unless * the logging buffer is empty. */ if( bufferPosition > 0 ) { pLoggingBuffer[ bufferPosition ] = ' '; bufferPosition++; } va_start( args, pFormat ); /* Add the log message to the logging buffer. */ requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition, bufferSize - bufferPosition, pFormat, args ); va_end( args ); /* If the logging buffer was too small to fit the log message, reallocate * a larger logging buffer. */ if( ( size_t ) requiredMessageSize >= bufferSize - bufferPosition ) { #if IOT_STATIC_MEMORY_ONLY == 1 /* There's no point trying to allocate a larger static buffer. Return * immediately. */ IotLogging_Free( pLoggingBuffer ); return; #else if( _reallocLoggingBuffer( ( void ** ) &pLoggingBuffer, ( size_t ) requiredMessageSize + bufferPosition + 1, bufferSize ) == false ) { /* If buffer reallocation failed, return. */ IotLogging_Free( pLoggingBuffer ); return; } /* Reallocation successful, update buffer size. */ bufferSize = ( size_t ) requiredMessageSize + bufferPosition + 1; /* Add the log message to the buffer. Now that the buffer has been * reallocated, this should succeed. */ va_start( args, pFormat ); requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition, bufferSize - bufferPosition, pFormat, args ); va_end( args ); #endif /* if IOT_STATIC_MEMORY_ONLY == 1 */ } /* Check for encoding errors. */ if( requiredMessageSize <= 0 ) { IotLogging_Free( pLoggingBuffer ); return; } /* Print the logging buffer to stdout. */ IotLogging_Puts( pLoggingBuffer ); /* Free the logging buffer. */ IotLogging_Free( pLoggingBuffer ); } /*-----------------------------------------------------------*/ void IotLog_GenericPrintBuffer( const char * const pLibraryName, const char * const pHeader, const uint8_t * const pBuffer, size_t bufferSize ) { size_t i = 0, offset = 0; /* Allocate memory to hold each line of the log message. Since each byte * of pBuffer is printed in 4 characters (2 digits, a space, and a null- * terminator), the size of each line is 4 * BYTES_PER_LINE. */ char * pMessageBuffer = IotLogging_Malloc( 4 * BYTES_PER_LINE ); /* Exit if no memory is available. */ if( pMessageBuffer == NULL ) { return; } /* Print pHeader before printing pBuffer. */ if( pHeader != NULL ) { IotLog_Generic( IOT_LOG_DEBUG, pLibraryName, IOT_LOG_DEBUG, NULL, pHeader ); } /* Print each byte in pBuffer. */ for( i = 0; i < bufferSize; i++ ) { /* Print a line if BYTES_PER_LINE is reached. But don't print a line * at the beginning (when i=0). */ if( ( i % BYTES_PER_LINE == 0 ) && ( i != 0 ) ) { IotLogging_Puts( pMessageBuffer ); /* Reset offset so that pMessageBuffer is filled from the beginning. */ offset = 0; } /* Print a single byte into pMessageBuffer. */ ( void ) snprintf( pMessageBuffer + offset, 4, "%02x ", pBuffer[ i ] ); /* Move the offset where the next character is printed. */ offset += 3; } /* Print the final line of bytes. This line isn't printed by the for-loop above. */ IotLogging_Puts( pMessageBuffer ); /* Free memory used by this function. */ IotLogging_Free( pMessageBuffer ); } /*-----------------------------------------------------------*/