]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/standard/common/src/iot_logging.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / c_sdk / standard / common / src / iot_logging.c
1 /*\r
2  * IoT Common V1.1.0\r
3  * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  */\r
22 \r
23 /**\r
24  * @file iot_logging.c\r
25  * @brief Implementation of logging functions from iot_logging.h\r
26  */\r
27 \r
28 /* The config header is always included first. */\r
29 #include "iot_config.h"\r
30 \r
31 /* Standard includes. */\r
32 #include <stdarg.h>\r
33 #include <stdio.h>\r
34 #include <string.h>\r
35 \r
36 /* Platform clock include. */\r
37 #include "platform/iot_clock.h"\r
38 \r
39 /* Logging includes. */\r
40 #include "iot_logging.h"\r
41 \r
42 /*-----------------------------------------------------------*/\r
43 \r
44 /* This implementation assumes the following values for the log level constants.\r
45  * Ensure that the values have not been modified. */\r
46 #if IOT_LOG_NONE != 0\r
47     #error "IOT_LOG_NONE must be 0."\r
48 #endif\r
49 #if IOT_LOG_ERROR != 1\r
50     #error "IOT_LOG_ERROR must be 1."\r
51 #endif\r
52 #if IOT_LOG_WARN != 2\r
53     #error "IOT_LOG_WARN must be 2."\r
54 #endif\r
55 #if IOT_LOG_INFO != 3\r
56     #error "IOT_LOG_INFO must be 3."\r
57 #endif\r
58 #if IOT_LOG_DEBUG != 4\r
59     #error "IOT_LOG_DEBUG must be 4."\r
60 #endif\r
61 \r
62 /**\r
63  * @def IotLogging_Puts( message )\r
64  * @brief Function the logging library uses to print a line.\r
65  *\r
66  * This function can be set by using a define. By default, the standard library\r
67  * [puts](http://pubs.opengroup.org/onlinepubs/9699919799/functions/puts.html)\r
68  * function is used.\r
69  */\r
70 #ifndef IotLogging_Puts\r
71     #define IotLogging_Puts    puts\r
72 #endif\r
73 \r
74 /*\r
75  * Provide default values for undefined memory allocation functions based on\r
76  * the usage of dynamic memory allocation.\r
77  */\r
78 #if IOT_STATIC_MEMORY_ONLY == 1\r
79     /* Static memory allocation header. */\r
80     #include "iot_static_memory.h"\r
81 \r
82 /**\r
83  * @brief Allocate a new logging buffer. This function must have the same\r
84  * signature as [malloc](http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).\r
85  */\r
86     #ifndef IotLogging_Malloc\r
87         #define IotLogging_Malloc    Iot_MallocMessageBuffer\r
88     #endif\r
89 \r
90 /**\r
91  * @brief Free a logging buffer. This function must have the same signature\r
92  * as [free](http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).\r
93  */\r
94     #ifndef IotLogging_Free\r
95         #define IotLogging_Free    Iot_FreeMessageBuffer\r
96     #endif\r
97 \r
98 /**\r
99  * @brief Get the size of a logging buffer. Statically-allocated buffers\r
100  * should all have the same size.\r
101  */\r
102     #ifndef IotLogging_StaticBufferSize\r
103         #define IotLogging_StaticBufferSize    Iot_MessageBufferSize\r
104     #endif\r
105 #else /* if IOT_STATIC_MEMORY_ONLY == 1 */\r
106     #ifndef IotLogging_Malloc\r
107         #include <stdlib.h>\r
108         #define IotLogging_Malloc    malloc\r
109     #endif\r
110 \r
111     #ifndef IotLogging_Free\r
112         #include <stdlib.h>\r
113         #define IotLogging_Free    free\r
114     #endif\r
115 #endif /* if IOT_STATIC_MEMORY_ONLY == 1 */\r
116 \r
117 /**\r
118  * @brief A guess of the maximum length of a timestring.\r
119  *\r
120  * There's no way for this logging library to know the length of a timestring\r
121  * before it's generated. Therefore, the logging library will assume a maximum\r
122  * length of any timestring it may get. This value should be generous enough\r
123  * to accommodate the vast majority of timestrings.\r
124  *\r
125  * @see @ref platform_clock_function_gettimestring\r
126  */\r
127 #define MAX_TIMESTRING_LENGTH    ( 64 )\r
128 \r
129 /**\r
130  * @brief The longest string in #_pLogLevelStrings (below), plus 3 to accommodate\r
131  * `[]` and a null-terminator.\r
132  */\r
133 #define MAX_LOG_LEVEL_LENGTH     ( 8 )\r
134 \r
135 /**\r
136  * @brief How many bytes @ref logging_function_genericprintbuffer should output on\r
137  * each line.\r
138  */\r
139 #define BYTES_PER_LINE           ( 16 )\r
140 \r
141 /*-----------------------------------------------------------*/\r
142 \r
143 /**\r
144  * @brief Lookup table for log levels.\r
145  *\r
146  * Converts one of the @ref logging_constants_levels to a string.\r
147  */\r
148 static const char * const _pLogLevelStrings[ 5 ] =\r
149 {\r
150     "",      /* IOT_LOG_NONE */\r
151     "ERROR", /* IOT_LOG_ERROR */\r
152     "WARN ", /* IOT_LOG_WARN */\r
153     "INFO ", /* IOT_LOG_INFO */\r
154     "DEBUG"  /* IOT_LOG_DEBUG */\r
155 };\r
156 \r
157 /*-----------------------------------------------------------*/\r
158 \r
159 #if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 )\r
160     static bool _reallocLoggingBuffer( void ** pOldBuffer,\r
161                                        size_t newSize,\r
162                                        size_t oldSize )\r
163     {\r
164         bool status = false;\r
165 \r
166         /* Allocate a new, larger buffer. */\r
167         void * pNewBuffer = IotLogging_Malloc( newSize );\r
168 \r
169         /* Ensure that memory allocation succeeded. */\r
170         if( pNewBuffer != NULL )\r
171         {\r
172             /* Copy the data from the old buffer to the new buffer. */\r
173             ( void ) memcpy( pNewBuffer, *pOldBuffer, oldSize );\r
174 \r
175             /* Free the old buffer and update the pointer. */\r
176             IotLogging_Free( *pOldBuffer );\r
177             *pOldBuffer = pNewBuffer;\r
178 \r
179             status = true;\r
180         }\r
181 \r
182         return status;\r
183     }\r
184 #endif /* if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 ) */\r
185 \r
186 /*-----------------------------------------------------------*/\r
187 \r
188 void IotLog_Generic( int libraryLogSetting,\r
189                      const char * const pLibraryName,\r
190                      int messageLevel,\r
191                      const IotLogConfig_t * const pLogConfig,\r
192                      const char * const pFormat,\r
193                      ... )\r
194 {\r
195     int requiredMessageSize = 0;\r
196     size_t bufferSize = 0,\r
197            bufferPosition = 0, timestringLength = 0;\r
198     char * pLoggingBuffer = NULL;\r
199     va_list args;\r
200 \r
201     /* If the library's log level setting is lower than the message level,\r
202      * return without doing anything. */\r
203     if( ( messageLevel == 0 ) || ( messageLevel > libraryLogSetting ) )\r
204     {\r
205         return;\r
206     }\r
207 \r
208     if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) )\r
209     {\r
210         /* Add length of log level if requested. */\r
211         bufferSize += MAX_LOG_LEVEL_LENGTH;\r
212     }\r
213 \r
214     /* Estimate the amount of buffer needed for this log message. */\r
215     if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) )\r
216     {\r
217         /* Add size of library name if requested. Add 2 to accommodate "[]". */\r
218         bufferSize += strlen( pLibraryName ) + 2;\r
219     }\r
220 \r
221     if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) )\r
222     {\r
223         /* Add length of timestring if requested. */\r
224         bufferSize += MAX_TIMESTRING_LENGTH;\r
225     }\r
226 \r
227     /* Add 64 as an initial (arbitrary) guess for the length of the message. */\r
228     bufferSize += 64;\r
229 \r
230     /* In static memory mode, check that the log message will fit in the a\r
231      * static buffer. */\r
232     #if IOT_STATIC_MEMORY_ONLY == 1\r
233         if( bufferSize >= IotLogging_StaticBufferSize() )\r
234         {\r
235             /* If the static buffers are likely too small to fit the log message,\r
236              * return. */\r
237             return;\r
238         }\r
239 \r
240         /* Otherwise, update the buffer size to the size of a static buffer. */\r
241         bufferSize = IotLogging_StaticBufferSize();\r
242     #endif\r
243 \r
244     /* Allocate memory for the logging buffer. */\r
245     pLoggingBuffer = ( char * ) IotLogging_Malloc( bufferSize );\r
246 \r
247     if( pLoggingBuffer == NULL )\r
248     {\r
249         return;\r
250     }\r
251 \r
252     /* Print the message log level if requested. */\r
253     if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) )\r
254     {\r
255         /* Ensure that message level is valid. */\r
256         if( ( messageLevel >= IOT_LOG_NONE ) && ( messageLevel <= IOT_LOG_DEBUG ) )\r
257         {\r
258             /* Add the log level string to the logging buffer. */\r
259             requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition,\r
260                                             bufferSize - bufferPosition,\r
261                                             "[%s]",\r
262                                             _pLogLevelStrings[ messageLevel ] );\r
263 \r
264             /* Check for encoding errors. */\r
265             if( requiredMessageSize <= 0 )\r
266             {\r
267                 IotLogging_Free( pLoggingBuffer );\r
268 \r
269                 return;\r
270             }\r
271 \r
272             /* Update the buffer position. */\r
273             bufferPosition += ( size_t ) requiredMessageSize;\r
274         }\r
275     }\r
276 \r
277     /* Print the library name if requested. */\r
278     if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) )\r
279     {\r
280         /* Add the library name to the logging buffer. */\r
281         requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition,\r
282                                         bufferSize - bufferPosition,\r
283                                         "[%s]",\r
284                                         pLibraryName );\r
285 \r
286         /* Check for encoding errors. */\r
287         if( requiredMessageSize <= 0 )\r
288         {\r
289             IotLogging_Free( pLoggingBuffer );\r
290 \r
291             return;\r
292         }\r
293 \r
294         /* Update the buffer position. */\r
295         bufferPosition += ( size_t ) requiredMessageSize;\r
296     }\r
297 \r
298     /* Print the timestring if requested. */\r
299     if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) )\r
300     {\r
301         /* Add the opening '[' enclosing the timestring. */\r
302         pLoggingBuffer[ bufferPosition ] = '[';\r
303         bufferPosition++;\r
304 \r
305         /* Generate the timestring and add it to the buffer. */\r
306         if( IotClock_GetTimestring( pLoggingBuffer + bufferPosition,\r
307                                     bufferSize - bufferPosition,\r
308                                     &timestringLength ) == true )\r
309         {\r
310             /* If the timestring was successfully generated, add the closing "]". */\r
311             bufferPosition += timestringLength;\r
312             pLoggingBuffer[ bufferPosition ] = ']';\r
313             bufferPosition++;\r
314         }\r
315         else\r
316         {\r
317             /* Sufficient memory for a timestring should have been allocated. A timestring\r
318              * probably failed to generate due to a clock read error; remove the opening '['\r
319              * from the logging buffer. */\r
320             bufferPosition--;\r
321             pLoggingBuffer[ bufferPosition ] = '\0';\r
322         }\r
323     }\r
324 \r
325     /* Add a padding space between the last closing ']' and the message, unless\r
326      * the logging buffer is empty. */\r
327     if( bufferPosition > 0 )\r
328     {\r
329         pLoggingBuffer[ bufferPosition ] = ' ';\r
330         bufferPosition++;\r
331     }\r
332 \r
333     va_start( args, pFormat );\r
334 \r
335     /* Add the log message to the logging buffer. */\r
336     requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition,\r
337                                      bufferSize - bufferPosition,\r
338                                      pFormat,\r
339                                      args );\r
340 \r
341     va_end( args );\r
342 \r
343     /* If the logging buffer was too small to fit the log message, reallocate\r
344      * a larger logging buffer. */\r
345     if( ( size_t ) requiredMessageSize >= bufferSize - bufferPosition )\r
346     {\r
347         #if IOT_STATIC_MEMORY_ONLY == 1\r
348 \r
349             /* There's no point trying to allocate a larger static buffer. Return\r
350              * immediately. */\r
351             IotLogging_Free( pLoggingBuffer );\r
352 \r
353             return;\r
354         #else\r
355             if( _reallocLoggingBuffer( ( void ** ) &pLoggingBuffer,\r
356                                        ( size_t ) requiredMessageSize + bufferPosition + 1,\r
357                                        bufferSize ) == false )\r
358             {\r
359                 /* If buffer reallocation failed, return. */\r
360                 IotLogging_Free( pLoggingBuffer );\r
361 \r
362                 return;\r
363             }\r
364 \r
365             /* Reallocation successful, update buffer size. */\r
366             bufferSize = ( size_t ) requiredMessageSize + bufferPosition + 1;\r
367 \r
368             /* Add the log message to the buffer. Now that the buffer has been\r
369              * reallocated, this should succeed. */\r
370             va_start( args, pFormat );\r
371             requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition,\r
372                                              bufferSize - bufferPosition,\r
373                                              pFormat,\r
374                                              args );\r
375             va_end( args );\r
376         #endif /* if IOT_STATIC_MEMORY_ONLY == 1 */\r
377     }\r
378 \r
379     /* Check for encoding errors. */\r
380     if( requiredMessageSize <= 0 )\r
381     {\r
382         IotLogging_Free( pLoggingBuffer );\r
383 \r
384         return;\r
385     }\r
386 \r
387     /* Print the logging buffer to stdout. */\r
388     IotLogging_Puts( pLoggingBuffer );\r
389 \r
390     /* Free the logging buffer. */\r
391     IotLogging_Free( pLoggingBuffer );\r
392 }\r
393 \r
394 /*-----------------------------------------------------------*/\r
395 \r
396 void IotLog_GenericPrintBuffer( const char * const pLibraryName,\r
397                                 const char * const pHeader,\r
398                                 const uint8_t * const pBuffer,\r
399                                 size_t bufferSize )\r
400 {\r
401     size_t i = 0, offset = 0;\r
402 \r
403     /* Allocate memory to hold each line of the log message. Since each byte\r
404      * of pBuffer is printed in 4 characters (2 digits, a space, and a null-\r
405      * terminator), the size of each line is 4 * BYTES_PER_LINE. */\r
406     char * pMessageBuffer = IotLogging_Malloc( 4 * BYTES_PER_LINE );\r
407 \r
408     /* Exit if no memory is available. */\r
409     if( pMessageBuffer == NULL )\r
410     {\r
411         return;\r
412     }\r
413 \r
414     /* Print pHeader before printing pBuffer. */\r
415     if( pHeader != NULL )\r
416     {\r
417         IotLog_Generic( IOT_LOG_DEBUG,\r
418                         pLibraryName,\r
419                         IOT_LOG_DEBUG,\r
420                         NULL,\r
421                         pHeader );\r
422     }\r
423 \r
424     /* Print each byte in pBuffer. */\r
425     for( i = 0; i < bufferSize; i++ )\r
426     {\r
427         /* Print a line if BYTES_PER_LINE is reached. But don't print a line\r
428          * at the beginning (when i=0). */\r
429         if( ( i % BYTES_PER_LINE == 0 ) && ( i != 0 ) )\r
430         {\r
431             IotLogging_Puts( pMessageBuffer );\r
432 \r
433             /* Reset offset so that pMessageBuffer is filled from the beginning. */\r
434             offset = 0;\r
435         }\r
436 \r
437         /* Print a single byte into pMessageBuffer. */\r
438         ( void ) snprintf( pMessageBuffer + offset, 4, "%02x ", pBuffer[ i ] );\r
439 \r
440         /* Move the offset where the next character is printed. */\r
441         offset += 3;\r
442     }\r
443 \r
444     /* Print the final line of bytes. This line isn't printed by the for-loop above. */\r
445     IotLogging_Puts( pMessageBuffer );\r
446 \r
447     /* Free memory used by this function. */\r
448     IotLogging_Free( pMessageBuffer );\r
449 }\r
450 \r
451 /*-----------------------------------------------------------*/\r