diff --git a/Kconfig b/Kconfig index 6ea56d39ac..1d214c7216 100644 --- a/Kconfig +++ b/Kconfig @@ -279,6 +279,16 @@ mainmenu "Espressif IoT Development Framework Configuration" endchoice # assertions + config COMPILER_OPTIMIZATION_CHECKS_SILENT + bool "Disable messages in ESP_RETURN_ON_* and ESP_EXIT_ON_* macros" + default n + help + If enabled, the error messages will be discarded in following check macros: + - ESP_RETURN_ON_ERROR + - ESP_EXIT_ON_ERROR + - ESP_RETURN_ON_FALSE + - ESP_EXIT_ON_FALSE + menuconfig COMPILER_HIDE_PATHS_MACROS bool "Replace ESP-IDF and project paths in binaries" default y diff --git a/components/esp_common/include/esp_check.h b/components/esp_common/include/esp_check.h new file mode 100644 index 0000000000..3ee631c394 --- /dev/null +++ b/components/esp_common/include/esp_check.h @@ -0,0 +1,188 @@ +// Copyright 2021 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "esp_err.h" +#include "esp_log.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Macro which can be used to check the error code. If the code is not ESP_OK, it prints the message and returns. + */ +#if defined(CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT) +#define ESP_RETURN_ON_ERROR(x, log_tag, format, ...) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + return err_rc_; \ + } \ + } while(0) +#else +#define ESP_RETURN_ON_ERROR(x, log_tag, format, ...) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + ESP_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + return err_rc_; \ + } \ + } while(0) +#endif + +/** + * A version of ESP_RETURN_ON_ERROR() macro that can be called from ISR. + */ +#if defined(CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT) +#define ESP_RETURN_ON_ERROR_ISR(x, log_tag, format, ...) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + return err_rc_; \ + } \ + } while(0) +#else +#define ESP_RETURN_ON_ERROR_ISR(x, log_tag, format, ...) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + ESP_EARLY_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + return err_rc_; \ + } \ + } while(0) +#endif + +/** + * Macro which can be used to check the error code. If the code is not ESP_OK, it prints the message, + * sets the local variable 'ret' to the code, and then exits by jumping to 'goto_tag'. + */ +#if defined(CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT) +#define ESP_GOTO_ON_ERROR(x, goto_tag, log_tag, format, ...) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + ret = err_rc_; \ + goto goto_tag; \ + } \ + } while(0) +#else +#define ESP_GOTO_ON_ERROR(x, goto_tag, log_tag, format, ...) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + ESP_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + ret = err_rc_; \ + goto goto_tag; \ + } \ + } while(0) +#endif + +/** + * A version of ESP_GOTO_ON_ERROR() macro that can be called from ISR. + */ +#if defined(CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT) +#define ESP_GOTO_ON_ERROR_ISR(x, goto_tag, log_tag, format, ...) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + ret = err_rc_; \ + goto goto_tag; \ + } \ + } while(0) +#else +#define ESP_GOTO_ON_ERROR_ISR(x, goto_tag, log_tag, format, ...) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + ESP_EARLY_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + ret = err_rc_; \ + goto goto_tag; \ + } \ + } while(0) +#endif + +/** + * Macro which can be used to check the condition. If the condition is not 'true', it prints the message + * and returns with the supplied 'err_code'. + */ +#if defined(CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT) +#define ESP_RETURN_ON_FALSE(a, err_code, log_tag, format, ...) do { \ + if (unlikely(!(a))) { \ + return err_code; \ + } \ + } while(0) +#else +#define ESP_RETURN_ON_FALSE(a, err_code, log_tag, format, ...) do { \ + if (unlikely(!(a))) { \ + ESP_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + return err_code; \ + } \ + } while(0) +#endif + +/** + * A version of ESP_RETURN_ON_FALSE() macro that can be called from ISR. + */ +#if defined(CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT) +#define ESP_RETURN_ON_FALSE_ISR(a, err_code, log_tag, format, ...) do { \ + if (unlikely(!(a))) { \ + return err_code; \ + } \ + } while(0) +#else +#define ESP_RETURN_ON_FALSE_ISR(a, err_code, log_tag, format, ...) do { \ + if (unlikely(!(a))) { \ + ESP_EARLY_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + return err_code; \ + } \ + } while(0) +#endif + +/** + * Macro which can be used to check the condition. If the condition is not 'true', it prints the message, + * sets the local variable 'ret' to the supplied 'err_code', and then exits by jumping to 'goto_tag'. + */ +#if defined(CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT) +#define ESP_GOTO_ON_FALSE(a, err_code, goto_tag, log_tag, format, ...) do { \ + if (unlikely(!(a))) { \ + ret = err_code; \ + goto goto_tag; \ + } \ + } while (0) +#else +#define ESP_GOTO_ON_FALSE(a, err_code, goto_tag, log_tag, format, ...) do { \ + if (unlikely(!(a))) { \ + ESP_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + ret = err_code; \ + goto goto_tag; \ + } \ + } while (0) +#endif + +/** + * A version of ESP_GOTO_ON_FALSE() macro that can be called from ISR. + */ +#if defined(CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT) +#define ESP_GOTO_ON_FALSE_ISR(a, err_code, goto_tag, log_tag, format, ...) do { \ + if (unlikely(!(a))) { \ + ret = err_code; \ + goto goto_tag; \ + } \ + } while (0) +#else +#define ESP_GOTO_ON_FALSE_ISR(a, err_code, goto_tag, log_tag, format, ...) do { \ + if (unlikely(!(a))) { \ + ESP_EARLY_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ + ret = err_code; \ + goto goto_tag; \ + } \ + } while (0) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_common/include/esp_err.h b/components/esp_common/include/esp_err.h index 28f98c9207..3a1c8c6295 100644 --- a/components/esp_common/include/esp_err.h +++ b/components/esp_common/include/esp_err.h @@ -16,6 +16,7 @@ #include #include #include +#include "esp_compiler.h" #ifdef __cplusplus extern "C" { @@ -104,21 +105,21 @@ void _esp_error_check_failed_without_abort(esp_err_t rc, const char *file, int l */ #ifdef NDEBUG #define ESP_ERROR_CHECK(x) do { \ - esp_err_t __err_rc = (x); \ - (void) sizeof(__err_rc); \ + esp_err_t err_rc_ = (x); \ + (void) sizeof(err_rc_); \ } while(0) #elif defined(CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT) #define ESP_ERROR_CHECK(x) do { \ - esp_err_t __err_rc = (x); \ - if (__err_rc != ESP_OK) { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ abort(); \ } \ } while(0) #else #define ESP_ERROR_CHECK(x) do { \ - esp_err_t __err_rc = (x); \ - if (__err_rc != ESP_OK) { \ - _esp_error_check_failed(__err_rc, __FILE__, __LINE__, \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + _esp_error_check_failed(err_rc_, __FILE__, __LINE__, \ __ASSERT_FUNC, #x); \ } \ } while(0) @@ -131,17 +132,17 @@ void _esp_error_check_failed_without_abort(esp_err_t rc, const char *file, int l */ #ifdef NDEBUG #define ESP_ERROR_CHECK_WITHOUT_ABORT(x) ({ \ - esp_err_t __err_rc = (x); \ - __err_rc; \ + esp_err_t err_rc_ = (x); \ + err_rc_; \ }) #else #define ESP_ERROR_CHECK_WITHOUT_ABORT(x) ({ \ - esp_err_t __err_rc = (x); \ - if (__err_rc != ESP_OK) { \ - _esp_error_check_failed_without_abort(__err_rc, __FILE__, __LINE__, \ - __ASSERT_FUNC, #x); \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + _esp_error_check_failed_without_abort(err_rc_, __FILE__, __LINE__, \ + __ASSERT_FUNC, #x); \ } \ - __err_rc; \ + err_rc_; \ }) #endif //NDEBUG diff --git a/docs/doxygen/Doxyfile_common b/docs/doxygen/Doxyfile_common index d77df83d48..2bb56ab17c 100644 --- a/docs/doxygen/Doxyfile_common +++ b/docs/doxygen/Doxyfile_common @@ -289,6 +289,8 @@ INPUT = \ $(IDF_PATH)/components/esp_ringbuf/include/freertos/ringbuf.h \ ### Helper functions for error codes $(IDF_PATH)/components/esp_common/include/esp_err.h \ + ### Check macros + $(IDF_PATH)/components/esp_common/include/esp_check.h \ ### System APIs $(IDF_PATH)/components/esp_system/include/esp_system.h \ ### Modbus controller component header file diff --git a/docs/en/api-guides/error-handling.rst b/docs/en/api-guides/error-handling.rst index 42c5e7985e..5b1e1f73f3 100644 --- a/docs/en/api-guides/error-handling.rst +++ b/docs/en/api-guides/error-handling.rst @@ -68,6 +68,71 @@ Error message will typically look like this:: - Finally, backtrace is printed. This is part of panic handler output common to all fatal errors. See :doc:`Fatal Errors ` for more information about the backtrace. +.. _esp-error-check-without-abort-macro: + +``ESP_ERROR_CHECK_WITHOUT_ABORT`` macro +--------------------------------------- + +:cpp:func:`ESP_ERROR_CHECK_WITHOUT_ABORT` macro serves similar purpose as ``ESP_ERROR_CHECK``, except that it won't call ``abort()``. + +.. _esp-return-on-error-macro: + +``ESP_RETURN_ON_ERROR`` macro +----------------------------- + +:cpp:func:`ESP_RETURN_ON_ERROR` macro checks the error code, if the error code is not equal :c:macro:`ESP_OK`, it prints the message and returns. + +.. _esp-goto-on-error-macro: + +``ESP_GOTO_ON_ERROR`` macro +--------------------------- + +:cpp:func:`ESP_GOTO_ON_ERROR` macro checks the error code, if the error code is not equal :c:macro:`ESP_OK`, it prints the message, sets the local variable `ret` to the code, and then exits by jumping to `goto_tag`. + +.. _esp-return-on-false-macro: + +``ESP_RETURN_ON_FALSE`` macro +----------------------------- + +:cpp:func:`ESP_RETURN_ON_FALSE` macro checks the condition, if the condition is not equal `true`, it prints the message and returns with the supplied `err_code`. + +.. _esp-goto-on-false-macro: + +``ESP_GOTO_ON_FALSE`` macro +--------------------------- + +:cpp:func:`ESP_GOTO_ON_FALSE` macro checks the condition, if the condition is not equal `true`, it prints the message, sets the local variable `ret` to the supplied `err_code`, and then exits by jumping to `goto_tag`. + +.. _check_macros_examples: + +``CHECK MACROS Examples`` +------------------------- + +Some examples:: + + static const char* TAG = "Test"; + + esp_err_t test_func(void) + { + esp_err_t ret = ESP_OK; + + ESP_ERROR_CHECK(x); // err message printed if `x` is not `ESP_OK`, and then `abort()`. + ESP_ERROR_CHECK_WITHOUT_ABORT(x); // err message printed if `x` is not `ESP_OK`, without `abort()`. + ESP_RETURN_ON_ERROR(x, TAG, "fail reason 1"); // err message printed if `x` is not `ESP_OK`, and then function returns with code `x`. + ESP_GOTO_ON_ERROR(x, err, TAG, "fail reason 2"); // err message printed if `x` is not `ESP_OK`, `ret` is set to `x`, and then jumps to `err`. + ESP_RETURN_ON_FALSE(a, err_code, TAG, "fail reason 3"); // err message printed if `a` is not `true`, and then function returns with code `err_code`. + ESP_GOTO_ON_FALSE(a, err_code, err, TAG, "fail reason 4"); // err message printed if `a` is not `true`, `ret` is set to `err_code`, and then jumps to `err`. + + err: + // clean up + return ret; + } + +.. note:: + + If the option :ref:`CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT` in Kconfig is enabled, the err message will be discarded, while the other action works as is. + + The ``ESP_RETURN_XX`` and ``ESP_GOTO_xx`` macros can't be called from ISR. While there are ``xx_ISR`` versions for each of them, e.g., `ESP_RETURN_ON_ERROR_ISR`, these macros could be used in ISR. Error handling patterns ----------------------- diff --git a/docs/zh_CN/api-guides/error-handling.rst b/docs/zh_CN/api-guides/error-handling.rst index 827b878f0a..3264c52305 100644 --- a/docs/zh_CN/api-guides/error-handling.rst +++ b/docs/zh_CN/api-guides/error-handling.rst @@ -69,6 +69,71 @@ ESP-IDF 中大多数函数会返回 :cpp:type:`esp_err_t` 类型的错误码, .. note:: 如果使用 :doc:`IDF monitor `, 则最后一行回溯结果中的地址将会被替换为相应的文件名和行号。 +.. _esp-error-check-without-abort-macro: + +``ESP_ERROR_CHECK_WITHOUT_ABORT`` 宏 +------------------------------------ + +宏 :cpp:func:`ESP_ERROR_CHECK_WITHOUT_ABORT` 的功能和 ``ESP_ERROR_CHECK`` 类似, 不同之处在于它不会调用 ``abort()``. + +.. _esp-return-on-error-macro: + +``ESP_RETURN_ON_ERROR`` 宏 +-------------------------- + +宏 :cpp:func:`ESP_RETURN_ON_ERROR` 用于错误码检查, 如果错误码不等于 :c:macro:`ESP_OK`, 该宏会打印错误信息,并使原函数立刻返回。 + +.. _esp-goto-on-error-macro: + +``ESP_GOTO_ON_ERROR`` 宏 +------------------------ + +宏 :cpp:func:`ESP_GOTO_ON_ERROR` 用于错误码检查, 如果错误码不等于 :c:macro:`ESP_OK`, 该宏会打印错误信息,将局部变量 `ret` 赋值为该错误码, 并使原函数跳转至给定的 `goto_tag`. + +.. _esp-return-on-false-macro: + +``ESP_RETURN_ON_FALSE`` 宏 +-------------------------- + +宏 :cpp:func:`ESP_RETURN_ON_FALSE` 用于条件检查, 如果给定条件不等于 `true`, 该宏会打印错误信息,并使原函数立刻返回,返回值为给定的 `err_code`. + +.. _esp-goto-on-false-macro: + +``ESP_GOTO_ON_FALSE`` 宏 +------------------------ + +宏 :cpp:func:`ESP_GOTO_ON_FALSE` 用于条件检查, 如果给定条件不等于 `true`, 该宏会打印错误信息,将局部变量 `ret` 赋值为给定的 `err_code`, 并使原函数跳转至给定的 `goto_tag`. + +.. _check_macros_examples: + +``CHECK 宏使用示例`` +------------------------- + +示例:: + + static const char* TAG = "Test"; + + esp_err_t test_func(void) + { + esp_err_t ret = ESP_OK; + + ESP_ERROR_CHECK(x); // err message printed if `x` is not `ESP_OK`, and then `abort()`. + ESP_ERROR_CHECK_WITHOUT_ABORT(x); // err message printed if `x` is not `ESP_OK`, without `abort()`. + ESP_RETURN_ON_ERROR(x, TAG, "fail reason 1"); // err message printed if `x` is not `ESP_OK`, and then function returns with code `x`. + ESP_GOTO_ON_ERROR(x, err, TAG, "fail reason 2"); // err message printed if `x` is not `ESP_OK`, `ret` is set to `x`, and then jumps to `err`. + ESP_RETURN_ON_FALSE(a, err_code, TAG, "fail reason 3"); // err message printed if `a` is not `true`, and then function returns with code `err_code`. + ESP_GOTO_ON_FALSE(a, err_code, err, TAG, "fail reason 4"); // err message printed if `a` is not `true`, `ret` is set to `err_code`, and then jumps to `err`. + + err: + // clean up + return ret; + } + +.. note:: + + 如果 Kconfig 中的 :ref:`CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT` 选项被打开, CHECK 宏将不会打印错误信息,其他功能不变。 + + ``ESP_RETURN_xx`` 和 ``ESP_GOTO_xx`` 宏不可以在中断服务程序里被调用。如需要在中断中使用类似功能,请使用 ``xx_ISR`` 宏,如 ``ESP_RETURN_ON_ERROR_ISR`` 等。 错误处理模式 ------------