diff --git a/components/log/CMakeLists.txt b/components/log/CMakeLists.txt index bd841ede36..32625fb94e 100644 --- a/components/log/CMakeLists.txt +++ b/components/log/CMakeLists.txt @@ -1,22 +1,27 @@ idf_build_get_property(target IDF_TARGET) -set(srcs "log.c" "log_buffers.c") + +set(srcs "") set(priv_requires "") -if(${target} STREQUAL "linux") - list(APPEND srcs "log_linux.c") + +if(BOOTLOADER_BUILD) + list(APPEND srcs "log_noos.c") else() - list(APPEND priv_requires soc hal esp_hw_support) + list(APPEND srcs "log.c") + + if(${target} STREQUAL "linux") + list(APPEND srcs "log_linux.c") + else() + list(APPEND srcs "log_freertos.c") + list(APPEND priv_requires soc hal esp_hw_support) + endif() + + # Buffer APIs call ESP_LOG_LEVEL -> esp_log_write, which can not used in bootloader. + list(APPEND srcs "src/buffer/log_buffers.c" + "src/util.c") endif() idf_component_register(SRCS ${srcs} INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "include/esp_private" LDFRAGMENTS linker.lf PRIV_REQUIRES ${priv_requires}) - -if(NOT ${target} STREQUAL "linux") - # Ideally, FreeRTOS shouldn't be included into bootloader build, so the 2nd check should be unnecessary - if(freertos IN_LIST BUILD_COMPONENTS AND NOT BOOTLOADER_BUILD) - target_sources(${COMPONENT_TARGET} PRIVATE log_freertos.c) - else() - target_sources(${COMPONENT_TARGET} PRIVATE log_noos.c) - endif() -endif() diff --git a/components/log/host_test/log_test/main/log_test.cpp b/components/log/host_test/log_test/main/log_test.cpp index a9c2a12435..c81282dcb0 100644 --- a/components/log/host_test/log_test/main/log_test.cpp +++ b/components/log/host_test/log_test/main/log_test.cpp @@ -10,6 +10,7 @@ #include #include #include "esp_log.h" +#include "esp_private/log_util.h" #include @@ -24,6 +25,7 @@ public: BasicLogFixture(esp_log_level_t log_level = ESP_LOG_VERBOSE) { std::memset(print_buffer, 0, BUFFER_SIZE); + buffer_idx = 0; esp_log_level_set("*", log_level); } @@ -40,11 +42,13 @@ public: void reset_buffer() { std::memset(print_buffer, 0, BUFFER_SIZE); + buffer_idx = 0; additional_reset(); } protected: char print_buffer [BUFFER_SIZE]; + int buffer_idx; virtual void additional_reset() { } }; @@ -75,7 +79,9 @@ private: int print_to_buffer(const char *format, va_list args) { - int ret = vsnprintf(print_buffer, BUFFER_SIZE, format, args); + // Added support for multi-line log, for example ESP_LOG_BUFFER... + int ret = vsnprintf(&print_buffer[buffer_idx], BUFFER_SIZE, format, args); + buffer_idx += ret; return ret; } @@ -215,6 +221,19 @@ TEST_CASE("log bytes > 127") CHECK(regex_search(fix.get_print_buffer_string(), buffer_regex)); } +TEST_CASE("log buffer char") +{ + PrintFixture fix(ESP_LOG_INFO); + const char g[] = "The way to get started is to quit talking and begin doing. - Walt Disney"; + const std::regex buffer_regex("I \\([0-9]*\\) test: The way to get s.*\n\ +.*I \\([0-9]*\\) test: tarted is to qui.*\n\ +.*I \\([0-9]*\\) test: t talking and be.*\n\ +.*I \\([0-9]*\\) test: gin doing. - Wal.*\n\ +.*I \\([0-9]*\\) test: t Disney", std::regex::ECMAScript); + ESP_LOG_BUFFER_CHAR(TEST_TAG, g, sizeof(g)); + CHECK(regex_search(fix.get_print_buffer_string(), buffer_regex) == true); +} + TEST_CASE("log buffer dump") { PrintFixture fix(ESP_LOG_INFO); @@ -303,3 +322,51 @@ TEST_CASE("changing early log level") ESP_EARLY_LOGI(TEST_TAG, "must indeed be printed"); CHECK(regex_search(fix.get_print_buffer_string(), test_print) == true); } + +TEST_CASE("esp_log_util_cvt") +{ + char buf[128]; + CHECK(esp_log_util_cvt_dec(123456, 0, buf) == 6); + CHECK(strcmp(buf, "123456") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_dec(123456, 2, buf) == 6); + CHECK(strcmp(buf, "123456") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_dec(123456, 8, buf) == 8); + CHECK(strcmp(buf, "00123456") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_dec(1, 0, buf) == 1); + CHECK(strcmp(buf, "1") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_dec(0, 0, buf) == 1); + CHECK(strcmp(buf, "0") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_dec(0, 4, buf) == 4); + CHECK(strcmp(buf, "0000") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_dec(1, 2, buf) == 2); + CHECK(strcmp(buf, "01") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_hex(0x73f, -1, buf) == 3); + CHECK(strcmp(buf, "73f") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_hex(0x73f, 2, buf) == 3); + CHECK(strcmp(buf, "73f") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_hex(0x73f, 3, buf) == 3); + CHECK(strcmp(buf, "73f") == 0); + memset(buf, 0, sizeof(buf)); + + CHECK(esp_log_util_cvt_hex(0x73f, 4, buf) == 4); + CHECK(strcmp(buf, "073f") == 0); + memset(buf, 0, sizeof(buf)); +} diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 51c474937b..325165111f 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,24 +12,13 @@ #include #include "sdkconfig.h" #include "esp_rom_sys.h" +#include "esp_log_level.h" +#include "esp_log_buffer.h" #ifdef __cplusplus extern "C" { #endif -/** - * @brief Log level - * - */ -typedef enum { - ESP_LOG_NONE, /*!< No log output */ - ESP_LOG_ERROR, /*!< Critical errors, software module can not recover on its own */ - ESP_LOG_WARN, /*!< Error conditions from which recovery measures have been taken */ - ESP_LOG_INFO, /*!< Information messages which describe normal flow of events */ - ESP_LOG_DEBUG, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */ - ESP_LOG_VERBOSE /*!< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */ -} esp_log_level_t; - typedef int (*vprintf_like_t)(const char *, va_list); /** @@ -172,113 +161,6 @@ void esp_log_writev(esp_log_level_t level, const char* tag, const char* format, /** @cond */ -#include "esp_log_internal.h" - -#ifndef LOG_LOCAL_LEVEL -#ifndef BOOTLOADER_BUILD -#define LOG_LOCAL_LEVEL CONFIG_LOG_MAXIMUM_LEVEL -#else -#define LOG_LOCAL_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL -#endif -#endif - -/** @endcond */ - -/** - * @brief Log a buffer of hex bytes at specified level, separated into 16 bytes each line. - * - * @param tag description tag - * @param buffer Pointer to the buffer array - * @param buff_len length of buffer in bytes - * @param level level of the log - * - */ -#define ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, level ) \ - do {\ - if ( LOG_LOCAL_LEVEL >= (level) ) { \ - esp_log_buffer_hex_internal( tag, buffer, buff_len, level ); \ - } \ - } while(0) - -/** - * @brief Log a buffer of characters at specified level, separated into 16 bytes each line. Buffer should contain only printable characters. - * - * @param tag description tag - * @param buffer Pointer to the buffer array - * @param buff_len length of buffer in bytes - * @param level level of the log - * - */ -#define ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, level ) \ - do {\ - if ( LOG_LOCAL_LEVEL >= (level) ) { \ - esp_log_buffer_char_internal( tag, buffer, buff_len, level ); \ - } \ - } while(0) - -/** - * @brief Dump a buffer to the log at specified level. - * - * The dump log shows just like the one below: - * - * W (195) log_example: 0x3ffb4280 45 53 50 33 32 20 69 73 20 67 72 65 61 74 2c 20 |ESP32 is great, | - * W (195) log_example: 0x3ffb4290 77 6f 72 6b 69 6e 67 20 61 6c 6f 6e 67 20 77 69 |working along wi| - * W (205) log_example: 0x3ffb42a0 74 68 20 74 68 65 20 49 44 46 2e 00 |th the IDF..| - * - * It is highly recommended to use terminals with over 102 text width. - * - * @param tag description tag - * @param buffer Pointer to the buffer array - * @param buff_len length of buffer in bytes - * @param level level of the log - */ -#define ESP_LOG_BUFFER_HEXDUMP( tag, buffer, buff_len, level ) \ - do { \ - if ( LOG_LOCAL_LEVEL >= (level) ) { \ - esp_log_buffer_hexdump_internal( tag, buffer, buff_len, level); \ - } \ - } while(0) - -/** - * @brief Log a buffer of hex bytes at Info level - * - * @param tag description tag - * @param buffer Pointer to the buffer array - * @param buff_len length of buffer in bytes - * - * @see ``esp_log_buffer_hex_level`` - * - */ -#define ESP_LOG_BUFFER_HEX(tag, buffer, buff_len) \ - do { \ - if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) { \ - ESP_LOG_BUFFER_HEX_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO ); \ - }\ - } while(0) - -/** - * @brief Log a buffer of characters at Info level. Buffer should contain only printable characters. - * - * @param tag description tag - * @param buffer Pointer to the buffer array - * @param buff_len length of buffer in bytes - * - * @see ``esp_log_buffer_char_level`` - * - */ -#define ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len) \ - do { \ - if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) { \ - ESP_LOG_BUFFER_CHAR_LEVEL( tag, buffer, buff_len, ESP_LOG_INFO ); \ - }\ - } while(0) - -/** @cond */ - -//to be back compatible -#define esp_log_buffer_hex ESP_LOG_BUFFER_HEX -#define esp_log_buffer_char ESP_LOG_BUFFER_CHAR - #if CONFIG_LOG_COLORS #define LOG_COLOR_BLACK "30" #define LOG_COLOR_RED "31" diff --git a/components/log/include/esp_log_buffer.h b/components/log/include/esp_log_buffer.h new file mode 100644 index 0000000000..6d0d0f0042 --- /dev/null +++ b/components/log/include/esp_log_buffer.h @@ -0,0 +1,180 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_log_level.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !BOOTLOADER_BUILD || __DOXYGEN__ + +/** + * @brief Logs a buffer of hexadecimal bytes at the specified log level. + * + * This function logs a buffer of hexadecimal bytes with 16 bytes per line. The + * log level determines the severity of the log message. + * + * @note This function does not check the log level against the ESP_LOCAL_LEVEL. + * The log level comparison should be done in esp_log.h. + * + * @param tag Description tag to identify the log. + * @param buffer Pointer to the buffer array containing the data to be logged. + * @param buff_len Length of the buffer in bytes. + * @param level Log level indicating the severity of the log message. +*/ +void esp_log_buffer_hex_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t level); + +/** + * @brief This function logs a buffer of characters with 16 characters per line. + * The buffer should contain only printable characters. The log level determines + * the severity of the log message. + * + * @note This function does not check the log level against the ESP_LOCAL_LEVEL. + * The log level comparison should be done in esp_log.h. + * + * @param tag Description tag to identify the log. + * @param buffer Pointer to the buffer array containing the data to be logged. + * @param buff_len Length of the buffer in bytes. + * @param level Log level indicating the severity of the log message. +*/ +void esp_log_buffer_char_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t level); + +/** + * @brief This function dumps a buffer to the log in a formatted hex dump style, + * displaying both the memory address and the corresponding hex and ASCII values + * of the bytes. The log level determines the severity of the log message. + * + * @note This function does not check the log level against the ESP_LOCAL_LEVEL. + * The log level comparison should be done in esp_log.h. + * @note It is recommended to use terminals with a width of at least 102 + * characters to display the log dump properly. + * + * @param tag Description tag to identify the log. + * @param buffer Pointer to the buffer array containing the data to be logged. + * @param buff_len Length of the buffer in bytes. + * @param log_level Log level indicating the severity of the log message. +*/ +void esp_log_buffer_hexdump_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level); + +/** + * @brief Log a buffer of hex bytes at specified level, separated into 16 bytes each line. + * + * The hex log shows just like the one below: + * + * I (954) log_example: 54 68 65 20 77 61 79 20 74 6f 20 67 65 74 20 73 + * I (962) log_example: 74 61 72 74 65 64 20 69 73 20 74 6f 20 71 75 69 + * I (969) log_example: 74 20 74 61 6c 6b 69 6e 67 20 61 6e 64 20 62 65 + * I (977) log_example: 67 69 6e 20 64 6f 69 6e 67 2e 20 2d 20 57 61 6c + * I (984) log_example: 74 20 44 69 73 6e 65 79 00 + * + * @param tag Description tag to identify the log. + * @param buffer Pointer to the buffer array containing the data to be logged. + * @param buff_len Length of the buffer in bytes. + * @param level Log level + */ +#define ESP_LOG_BUFFER_HEX_LEVEL(tag, buffer, buff_len, level) \ + do { if (LOG_LOCAL_LEVEL >= (level)) {esp_log_buffer_hex_internal(tag, buffer, buff_len, level);} } while(0) + +/** + * @brief Log a buffer of characters at specified level, separated into 16 bytes each line. Buffer should contain only printable characters. + * + * The char log shows just like the one below: + * + * I (980) log_example: The way to get s + * I (985) log_example: tarted is to qui + * I (989) log_example: t talking and be + * I (994) log_example: gin doing. - Wal + * I (999) log_example: t Disney + * + * @param tag Description tag to identify the log. + * @param buffer Pointer to the buffer array containing the data to be logged. + * @param buff_len Length of the buffer in bytes. + * @param level Log level. + * + */ +#define ESP_LOG_BUFFER_CHAR_LEVEL(tag, buffer, buff_len, level) \ + do { if (LOG_LOCAL_LEVEL >= (level)) {esp_log_buffer_char_internal(tag, buffer, buff_len, level);} } while(0) + +/** + * @brief Dump a buffer to the log at specified level. + * + * The dump log shows just like the one below: + * + * I (1013) log_example: 0x3ffb5bc0 54 68 65 20 77 61 79 20 74 6f 20 67 65 74 20 73 |The way to get s| + * I (1024) log_example: 0x3ffb5bd0 74 61 72 74 65 64 20 69 73 20 74 6f 20 71 75 69 |tarted is to qui| + * I (1034) log_example: 0x3ffb5be0 74 20 74 61 6c 6b 69 6e 67 20 61 6e 64 20 62 65 |t talking and be| + * I (1044) log_example: 0x3ffb5bf0 67 69 6e 20 64 6f 69 6e 67 2e 20 2d 20 57 61 6c |gin doing. - Wal| + * I (1054) log_example: 0x3ffb5c00 74 20 44 69 73 6e 65 79 00 |t Disney.| + * + * @note It is highly recommended to use terminals with over 102 text width. + * + * @param tag Description tag to identify the log. + * @param buffer Pointer to the buffer array containing the data to be logged. + * @param buff_len Length of the buffer in bytes. + * @param level Log level. + */ +#define ESP_LOG_BUFFER_HEXDUMP(tag, buffer, buff_len, level) \ + do { if (LOG_LOCAL_LEVEL >= (level)) {esp_log_buffer_hexdump_internal(tag, buffer, buff_len, level);} } while(0) + +/** + * @brief Log a buffer of hex bytes at Info level + * + * @param tag Description tag to identify the log. + * @param buffer Pointer to the buffer array containing the data to be logged. + * @param buff_len Length of the buffer in bytes. + * + * @see ``ESP_LOG_BUFFER_HEX_LEVEL`` + * + */ +#define ESP_LOG_BUFFER_HEX(tag, buffer, buff_len) \ + do { if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) {ESP_LOG_BUFFER_HEX_LEVEL(tag, buffer, buff_len, ESP_LOG_INFO);} } while(0) + +/** + * @brief Log a buffer of characters at Info level. Buffer should contain only printable characters. + * + * @param tag Description tag to identify the log. + * @param buffer Pointer to the buffer array containing the data to be logged. + * @param buff_len Length of the buffer in bytes. + * + * @see ``ESP_LOG_BUFFER_CHAR_LEVEL`` + * + */ +#define ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len) \ + do { if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) {ESP_LOG_BUFFER_CHAR_LEVEL(tag, buffer, buff_len, ESP_LOG_INFO);} } while(0) + +/** @cond */ +/** + * @note For back compatible + * @deprecated This function is deprecated and will be removed in the future. + * Please use ESP_LOG_BUFFER_HEX + */ +// __attribute__((deprecated("Use 'ESP_LOG_BUFFER_HEX' instead"))) +static inline void esp_log_buffer_hex(const char *tag, const void *buffer, uint16_t buff_len) +{ + ESP_LOG_BUFFER_HEX(tag, buffer, buff_len); +} + +/** + * @note For back compatible + * @deprecated This function is deprecated and will be removed in the future. + * Please use ESP_LOG_BUFFER_CHAR + */ +// __attribute__((deprecated("Use 'ESP_LOG_BUFFER_CHAR' instead"))) +static inline void esp_log_buffer_char(const char *tag, const void *buffer, uint16_t buff_len) +{ + ESP_LOG_BUFFER_CHAR(tag, buffer, buff_len); +} +/** @endcond */ + +#endif // !BOOTLOADER_BUILD || __DOXYGEN__ + +#ifdef __cplusplus +} +#endif diff --git a/components/log/include/esp_log_internal.h b/components/log/include/esp_log_internal.h index cbdb2db731..cee155fd64 100644 --- a/components/log/include/esp_log_internal.h +++ b/components/log/include/esp_log_internal.h @@ -1,15 +1,9 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef __ESP_LOG_INTERNAL_H__ -#define __ESP_LOG_INTERNAL_H__ - -//these functions do not check level versus ESP_LOCAL_LEVEL, this should be done in esp_log.h -void esp_log_buffer_hex_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t level); -void esp_log_buffer_char_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t level); -void esp_log_buffer_hexdump_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level); - -#endif +#pragma once +#warning "esp_log_internal.h is deprecated, please migrate to esp_log_buffer.h" +#include "esp_log_buffer.h" diff --git a/components/log/include/esp_log_level.h b/components/log/include/esp_log_level.h new file mode 100644 index 0000000000..5e25552a04 --- /dev/null +++ b/components/log/include/esp_log_level.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Log level + */ +typedef enum { + ESP_LOG_NONE = 0, /*!< No log output */ + ESP_LOG_ERROR = 1, /*!< Critical errors, software module can not recover on its own */ + ESP_LOG_WARN = 2, /*!< Error conditions from which recovery measures have been taken */ + ESP_LOG_INFO = 3, /*!< Information messages which describe normal flow of events */ + ESP_LOG_DEBUG = 4, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */ + ESP_LOG_VERBOSE = 5, /*!< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */ + ESP_LOG_MAX = 6, /*!< Number of levels supported */ +} esp_log_level_t; + +/** @cond */ + +#ifndef LOG_LOCAL_LEVEL +#ifndef BOOTLOADER_BUILD +#define LOG_LOCAL_LEVEL CONFIG_LOG_MAXIMUM_LEVEL +#else +#define LOG_LOCAL_LEVEL CONFIG_BOOTLOADER_LOG_LEVEL +#endif +#endif + +/** @endcond */ + +#ifdef __cplusplus +} +#endif diff --git a/components/log/include/esp_private/log_util.h b/components/log/include/esp_private/log_util.h new file mode 100644 index 0000000000..18f2b5d93e --- /dev/null +++ b/components/log/include/esp_private/log_util.h @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Convert an unsigned integer value to a string representation in the specified radix. + * + * This function converts the given unsigned integer value to a string representation in the specified radix. + * The resulting string is stored in the provided character buffer `buf`. + * + * @param[in] val The unsigned integer value to be converted. + * @param[in] radix The base of the numeral system to be used for the conversion. + * It determines the number of unique digits in the numeral system + * (e.g., 2 for binary, 10 for decimal, 16 for hexadecimal). + * @param[in] pad The optional padding width (0 - unused) for the resulting string. It adds zero-padding. + * (val=123, pad=6 -> result=000123). + * @param[in] digits Pointer to a character array representing the digits of the + * numeral system. The array must contain characters in the order of increasing + * values, corresponding to the digits of the radix. For example, "0123456789ABCDEF" + * or hexadecimal. + * @param[out] buf Pointer to the character buffer where the resulting string will + * be stored. The buffer must have enough space to accommodate the entire converted + * string, including the null-terminator. + * + * @return The length of the resulting string (excluding the null-terminator). + * + * @note The buffer `buf` must have sufficient space to hold the entire converted string, including the null-terminator. + * The caller is responsible for ensuring the buffer's size is large enough to prevent buffer overflow. + * @note The provided `digits` array must have enough elements to cover the entire radix used for conversion. Otherwise, undefined behavior may occur. + */ +int esp_log_util_cvt(unsigned long long val, long radix, int pad, const char *digits, char *buf); + +/** + * @brief Convert an unsigned integer to a hexadecimal string with optional padding. + * + * This function converts an unsigned integer value to a hexadecimal string representation. + * This function calls esp_log_util_cvt(val, 16, pad, "0123456789abcdef", buf) inside. + * + * @param val The unsigned integer value to be converted. + * @param pad The optional padding width for the resulting string. + * @param buf The buffer to store the hexadecimal string. + * @return The length of the converted string. + */ +int esp_log_util_cvt_hex(unsigned long long val, int pad, char *buf); + +/** + * @brief Convert an unsigned integer to a decimal string with optional padding. + * + * This function converts an unsigned integer value to a decimal string representation. + * This function calls esp_log_util_cvt(val, 10, pad, "0123456789", buf) inside. + * + * @param val The unsigned integer value to be converted. + * @param pad The optional padding width for the resulting string. + * @param buf The buffer to store the decimal string. + * @return The length of the converted string. + */ +int esp_log_util_cvt_dec(unsigned long long val, int pad, char *buf); + +#ifdef __cplusplus +} +#endif diff --git a/components/log/log_buffers.c b/components/log/log_buffers.c deleted file mode 100644 index bef3072455..0000000000 --- a/components/log/log_buffers.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include -#include "esp_log.h" - -#ifndef CONFIG_IDF_TARGET_LINUX -#include "esp_memory_utils.h" // for esp_ptr_byte_accessible -#else -static inline bool esp_ptr_byte_accessible(const void* ptr) -{ - (void) ptr; - return true; -} -#endif // CONFIG_IDF_TARGET_LINUX - -//print number of bytes per line for esp_log_buffer_char and esp_log_buffer_hex -#define BYTES_PER_LINE 16 - -void esp_log_buffer_hex_internal(const char *tag, const void *buffer, uint16_t buff_len, - esp_log_level_t log_level) -{ - if (buff_len == 0) { - return; - } - char temp_buffer[BYTES_PER_LINE + 3]; //for not-byte-accessible memory - char hex_buffer[3 * BYTES_PER_LINE + 1]; - const char *ptr_line; - int bytes_cur_line; - - do { - if (buff_len > BYTES_PER_LINE) { - bytes_cur_line = BYTES_PER_LINE; - } else { - bytes_cur_line = buff_len; - } - if (!esp_ptr_byte_accessible(buffer)) { - //use memcpy to get around alignment issue - memcpy(temp_buffer, buffer, (bytes_cur_line + 3) / 4 * 4); - ptr_line = temp_buffer; - } else { - ptr_line = buffer; - } - - for (int i = 0; i < bytes_cur_line; i ++) { - sprintf(hex_buffer + 3 * i, "%02x ", (unsigned char) ptr_line[i]); - } - ESP_LOG_LEVEL(log_level, tag, "%s", hex_buffer); - buffer += bytes_cur_line; - buff_len -= bytes_cur_line; - } while (buff_len); -} - -void esp_log_buffer_char_internal(const char *tag, const void *buffer, uint16_t buff_len, - esp_log_level_t log_level) -{ - if (buff_len == 0) { - return; - } - char temp_buffer[BYTES_PER_LINE + 3]; //for not-byte-accessible memory - char char_buffer[BYTES_PER_LINE + 1]; - const char *ptr_line; - int bytes_cur_line; - - do { - if (buff_len > BYTES_PER_LINE) { - bytes_cur_line = BYTES_PER_LINE; - } else { - bytes_cur_line = buff_len; - } - if (!esp_ptr_byte_accessible(buffer)) { - //use memcpy to get around alignment issue - memcpy(temp_buffer, buffer, (bytes_cur_line + 3) / 4 * 4); - ptr_line = temp_buffer; - } else { - ptr_line = buffer; - } - - for (int i = 0; i < bytes_cur_line; i ++) { - sprintf(char_buffer + i, "%c", ptr_line[i]); - } - ESP_LOG_LEVEL(log_level, tag, "%s", char_buffer); - buffer += bytes_cur_line; - buff_len -= bytes_cur_line; - } while (buff_len); -} - -void esp_log_buffer_hexdump_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level) -{ - - if (buff_len == 0) { - return; - } - char temp_buffer[BYTES_PER_LINE + 3]; //for not-byte-accessible memory - const char *ptr_line; - //format: field[length] - // ADDR[10]+" "+DATA_HEX[8*3]+" "+DATA_HEX[8*3]+" |"+DATA_CHAR[8]+"|" - char hd_buffer[2 + sizeof(void *) * 2 + 3 + BYTES_PER_LINE * 3 + 1 + 3 + BYTES_PER_LINE + 1 + 1]; - char *ptr_hd; - int bytes_cur_line; - - do { - if (buff_len > BYTES_PER_LINE) { - bytes_cur_line = BYTES_PER_LINE; - } else { - bytes_cur_line = buff_len; - } - if (!esp_ptr_byte_accessible(buffer)) { - //use memcpy to get around alignment issue - memcpy(temp_buffer, buffer, (bytes_cur_line + 3) / 4 * 4); - ptr_line = temp_buffer; - } else { - ptr_line = buffer; - } - ptr_hd = hd_buffer; - - ptr_hd += sprintf(ptr_hd, "%p ", buffer); - for (int i = 0; i < BYTES_PER_LINE; i ++) { - if ((i & 7) == 0) { - ptr_hd += sprintf(ptr_hd, " "); - } - if (i < bytes_cur_line) { - ptr_hd += sprintf(ptr_hd, " %02x", (unsigned char) ptr_line[i]); - } else { - ptr_hd += sprintf(ptr_hd, " "); - } - } - ptr_hd += sprintf(ptr_hd, " |"); - for (int i = 0; i < bytes_cur_line; i ++) { - if (isprint((int)ptr_line[i])) { - ptr_hd += sprintf(ptr_hd, "%c", ptr_line[i]); - } else { - ptr_hd += sprintf(ptr_hd, "."); - } - } - ptr_hd += sprintf(ptr_hd, "|"); - - ESP_LOG_LEVEL(log_level, tag, "%s", hd_buffer); - buffer += bytes_cur_line; - buff_len -= bytes_cur_line; - } while (buff_len); -} diff --git a/components/log/src/buffer/log_buffers.c b/components/log/src/buffer/log_buffers.c new file mode 100644 index 0000000000..88887ed667 --- /dev/null +++ b/components/log/src/buffer/log_buffers.c @@ -0,0 +1,137 @@ +/* + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_log.h" +#include "esp_private/log_util.h" + +#ifndef CONFIG_IDF_TARGET_LINUX +#include "esp_memory_utils.h" // for esp_ptr_byte_accessible +#else // !CONFIG_IDF_TARGET_LINUX +static inline bool esp_ptr_byte_accessible(const void* ptr) +{ + (void) ptr; + return true; +} +#endif // !CONFIG_IDF_TARGET_LINUX + +/* It represents the number of bytes printed per line when displaying the + * contents of a buffer in hexadecimal format. It determines how many bytes of + * the buffer will be shown on each line, making it easier to read and interpret + * the data. */ +#define BYTES_PER_LINE 16 + +/* Checks if a character is a printable ASCII character. + * If the provided character falls within the printable ASCII range, + * which includes characters with ASCII values from 32 (space) to 126 (tilde). */ +#define IS_CHAR_PRINTABLE(character) ((character) >= 32 && (character) <= 126) + +typedef void (*print_line_t)(uintptr_t, const void *, char *, int); + +static void log_buffer_hex_line(uintptr_t orig_buff, const void *ptr_line, char *output_str, int buff_len); +static void log_buffer_char_line(uintptr_t orig_buff, const void *ptr_line, char *output_str, int buff_len); +static void log_buffer_hexdump_line(uintptr_t orig_buff, const void *ptr_line, char *output_str, int buff_len); +static void print_buffer(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level, char *output_str, print_line_t print_line_func); + +void esp_log_buffer_hex_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level) +{ + // I (954) log_example: 54 68 65 20 77 61 79 20 74 6f 20 67 65 74 20 73 + // I (962) log_example: 74 61 72 74 65 64 20 69 73 20 74 6f 20 71 75 69 + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + char output_str[3 * BYTES_PER_LINE]; + print_buffer(tag, buffer, buff_len, log_level, output_str, log_buffer_hex_line); +} + +void esp_log_buffer_char_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level) +{ + // I (980) log_example: The way to get s + // I (985) log_example: tarted is to qui + // ^^^^^^^^^^^^^^^^^ + char output_str[BYTES_PER_LINE + 1]; + print_buffer(tag, buffer, buff_len, log_level, output_str, log_buffer_char_line); +} + +void esp_log_buffer_hexdump_internal(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level) +{ + // I (1013) log_example: 0x3ffb5bc0 54 68 65 20 77 61 79 20 74 6f 20 67 65 74 20 73 |The way to get s| + // I (1024) log_example: 0x3ffb5bd0 74 61 72 74 65 64 20 69 73 20 74 6f 20 71 75 69 |tarted is to qui| + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + char output_str[(2 + sizeof(void *) * 2) + 3 + (BYTES_PER_LINE * 3) + 2 + (1 + BYTES_PER_LINE + 1) + 1]; + print_buffer(tag, buffer, buff_len, log_level, output_str, log_buffer_hexdump_line); +} + +static void print_buffer(const char *tag, const void *buffer, uint16_t buff_len, esp_log_level_t log_level, char *output_str, print_line_t print_line_func) +{ + if (buff_len == 0) { + return; + } + char temp_buffer[BYTES_PER_LINE + 3]; //for not-byte-accessible memory + + do { + const char *ptr_line = buffer; + int bytes_cur_line = (buff_len > BYTES_PER_LINE) ? BYTES_PER_LINE : buff_len; + if (!esp_ptr_byte_accessible(buffer)) { + //use memcpy to get around alignment issue + memcpy(temp_buffer, buffer, (bytes_cur_line + 3) / 4 * 4); + ptr_line = temp_buffer; + } + + print_line_func((uintptr_t)buffer, ptr_line, output_str, bytes_cur_line); + + ESP_LOG_LEVEL(log_level, tag, "%s", output_str); + buffer += bytes_cur_line; + buff_len -= bytes_cur_line; + } while (buff_len); +} + +static void log_buffer_hex_line(uintptr_t orig_buff, const void *ptr_line, char *output_str, int buff_len) +{ + (void) orig_buff; + const unsigned char *ptr = (unsigned char *)ptr_line; + for (int i = 0; i < buff_len; i++) { + output_str += esp_log_util_cvt_hex(ptr[i], 2, output_str); + *output_str++ = ' '; + } + *--output_str = 0; +} + +static void log_buffer_char_line(uintptr_t orig_buff, const void *ptr_line, char *output_str, int buff_len) +{ + (void) orig_buff; + const char *ptr = (char *)ptr_line; + memcpy(output_str, ptr, buff_len); + output_str[buff_len] = 0; +} + +static void log_buffer_hexdump_line(uintptr_t orig_buff, const void *ptr_line, char *output_str, int buff_len) +{ + const unsigned char *ptr = (unsigned char *)ptr_line; + *output_str++ = '0'; + *output_str++ = 'x'; + output_str += esp_log_util_cvt_hex(orig_buff, sizeof(uintptr_t), output_str); + *output_str++ = ' '; + for (int i = 0; i < BYTES_PER_LINE; i++) { + if ((i & 7) == 0) { + *output_str++ = ' '; + } + *output_str++ = ' '; + if (i < buff_len) { + output_str += esp_log_util_cvt_hex(ptr[i], 2, output_str); + } else { + *output_str++ = ' '; + *output_str++ = ' '; + } + } + *output_str++ = ' '; + *output_str++ = ' '; + *output_str++ = '|'; + for (int i = 0; i < buff_len; i++) { + *output_str++ = IS_CHAR_PRINTABLE(ptr[i]) ? ptr[i] : '.'; + } + *output_str++ = '|'; + *output_str = 0; +} diff --git a/components/log/src/util.c b/components/log/src/util.c new file mode 100644 index 0000000000..675ee09b97 --- /dev/null +++ b/components/log/src/util.c @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int esp_log_util_cvt(unsigned long long val, long radix, int pad, const char *digits, char *buf) +{ + char *orig_buf = buf; + int length = 0; + + // val = 123 + do { + *buf++ = digits[val % radix]; + val /= radix; + length++; + } while (val); + // 3 2 1 + // buf = [0] [1] [2] [3] + + // length = 3, pad = 6 + while (pad > 0 && pad > length) { + *buf++ = '0'; + length++; + } + *buf = '\0'; + // length = 6 + // 3 2 1 0 0 0 \0 + // buf = [0] [1] [2] [3] [4] [5] [6] + + --buf; + // reverse the order of characters + // 3 2 1 0 0 0 \0 + // [0] [1] [2] [3] [4] [5] [6] + // orig_buf -- ^ ^ ----- buf + while (orig_buf < buf) { + char first_char = *orig_buf; + char last_char = *buf; + *buf-- = first_char; + *orig_buf++ = last_char; + } + // 0 0 0 1 2 3 \0 + // buf = [0] [1] [2] [3] [4] [5] [6] + + return (length); +} + +int esp_log_util_cvt_hex(unsigned long long val, int pad, char *buf) +{ + return esp_log_util_cvt(val, 16, pad, "0123456789abcdef", buf); +} + +int esp_log_util_cvt_dec(unsigned long long val, int pad, char *buf) +{ + return esp_log_util_cvt(val, 10, pad, "0123456789", buf); +} diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 7355144003..e8187b86df 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -267,6 +267,8 @@ INPUT = \ $(PROJECT_PATH)/components/ieee802154/include/esp_ieee802154_types.h \ $(PROJECT_PATH)/components/ieee802154/include/esp_ieee802154.h \ $(PROJECT_PATH)/components/log/include/esp_log.h \ + $(PROJECT_PATH)/components/log/include/esp_log_level.h \ + $(PROJECT_PATH)/components/log/include/esp_log_buffer.h \ $(PROJECT_PATH)/components/lwip/include/apps/esp_sntp.h \ $(PROJECT_PATH)/components/lwip/include/apps/ping/ping_sock.h \ $(PROJECT_PATH)/components/mbedtls/esp_crt_bundle/include/esp_crt_bundle.h \ diff --git a/docs/en/api-reference/system/log.rst b/docs/en/api-reference/system/log.rst index ccc4980e9e..d47e6d13cc 100644 --- a/docs/en/api-reference/system/log.rst +++ b/docs/en/api-reference/system/log.rst @@ -135,3 +135,5 @@ API Reference ------------- .. include-build-file:: inc/esp_log.inc +.. include-build-file:: inc/esp_log_level.inc +.. include-build-file:: inc/esp_log_buffer.inc