| /* |
| * FreeRTOS Common V1.1.1 |
| * 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 ); |
| } |
| |
| /*-----------------------------------------------------------*/ |