Merge branch 'doc/error_handling' into 'master'

docs: add information on error handling

See merge request idf/esp-idf!2539
This commit is contained in:
Ivan Grokhotkov 2018-06-15 16:55:33 +08:00
commit 4e243fb1c1
23 changed files with 649 additions and 166 deletions

129
Kconfig
View file

@ -35,134 +35,7 @@ endmenu # SDK tool configuration
source "$COMPONENT_KCONFIGS_PROJBUILD"
menu "Compiler options"
choice OPTIMIZATION_COMPILER
prompt "Optimization Level"
default OPTIMIZATION_LEVEL_DEBUG
help
This option sets compiler optimization level (gcc -O argument).
- for "Release" setting, -Os flag is added to CFLAGS.
- for "Debug" setting, -Og flag is added to CFLAGS.
"Release" with -Os produces smaller & faster compiled code but it
may be harder to correlated code addresses to source files when debugging.
To add custom optimization settings, set CFLAGS and/or CPPFLAGS
in project makefile, before including $(IDF_PATH)/make/project.mk. Note that
custom optimization levels may be unsupported.
config OPTIMIZATION_LEVEL_DEBUG
bool "Debug (-Og)"
config OPTIMIZATION_LEVEL_RELEASE
bool "Release (-Os)"
endchoice
choice OPTIMIZATION_ASSERTION_LEVEL
prompt "Assertion level"
default OPTIMIZATION_ASSERTIONS_ENABLED
help
Assertions can be:
- Enabled. Failure will print verbose assertion details. This is the default.
- Set to "silent" to save code size (failed assertions will abort() but user
needs to use the aborting address to find the line number with the failed assertion.)
- Disabled entirely (not recommended for most configurations.) -DNDEBUG is added
to CPPFLAGS in this case.
config OPTIMIZATION_ASSERTIONS_ENABLED
prompt "Enabled"
bool
help
Enable assertions. Assertion content and line number will be printed on failure.
config OPTIMIZATION_ASSERTIONS_SILENT
prompt "Silent (saves code size)"
bool
help
Enable silent assertions. Failed assertions will abort(), user needs to
use the aborting address to find the line number with the failed assertion.
config OPTIMIZATION_ASSERTIONS_DISABLED
prompt "Disabled (sets -DNDEBUG)"
bool
help
If assertions are disabled, -DNDEBUG is added to CPPFLAGS.
endchoice # assertions
menuconfig CXX_EXCEPTIONS
bool "Enable C++ exceptions"
default n
help
Enabling this option compiles all IDF C++ files with exception support enabled.
Disabling this option disables C++ exception support in all compiled files, and any libstdc++ code which throws
an exception will abort instead.
Enabling this option currently adds an additional ~500 bytes of heap overhead
when an exception is thrown in user code for the first time.
config CXX_EXCEPTIONS_EMG_POOL_SIZE
int "Emergency Pool Size"
default 0
depends on CXX_EXCEPTIONS
help
Size (in bytes) of the emergency memory pool for C++ exceptions. This pool will be used to allocate
memory for thrown exceptions when there is not enough memory on the heap.
choice STACK_CHECK_MODE
prompt "Stack smashing protection mode"
default STACK_CHECK_NONE
help
Stack smashing protection mode. Emit extra code to check for buffer overflows, such as stack
smashing attacks. This is done by adding a guard variable to functions with vulnerable objects.
The guards are initialized when a function is entered and then checked when the function exits.
If a guard check fails, program is halted. Protection has the following modes:
- In NORMAL mode (GCC flag: -fstack-protector) only functions that call alloca, and functions with buffers larger than
8 bytes are protected.
- STRONG mode (GCC flag: -fstack-protector-strong) is like NORMAL, but includes additional functions to be protected -- those that
have local array definitions, or have references to local frame addresses.
- In OVERALL mode (GCC flag: -fstack-protector-all) all functions are protected.
Modes have the following impact on code performance and coverage:
- performance: NORMAL > STRONG > OVERALL
- coverage: NORMAL < STRONG < OVERALL
config STACK_CHECK_NONE
bool "None"
config STACK_CHECK_NORM
bool "Normal"
config STACK_CHECK_STRONG
bool "Strong"
config STACK_CHECK_ALL
bool "Overall"
endchoice
config STACK_CHECK
bool
default !STACK_CHECK_NONE
help
Stack smashing protection.
config WARN_WRITE_STRINGS
bool "Enable -Wwrite-strings warning flag"
default "n"
help
Adds -Wwrite-strings flag for the C/C++ compilers.
For C, this gives string constants the type "const char[]" so that
copying the address of one into a non-"const" "char *" pointer
produces a warning. This warning helps to find at compile time code
that tries to write into a string constant.
For C++, this warns about the deprecated conversion from string
literals to "char *".
endmenu # Compiler Options
source "$IDF_PATH/Kconfig.compiler"
menu "Component config"
source "$COMPONENT_KCONFIGS"

135
Kconfig.compiler Normal file
View file

@ -0,0 +1,135 @@
menu "Compiler options"
choice OPTIMIZATION_COMPILER
prompt "Optimization Level"
default OPTIMIZATION_LEVEL_DEBUG
help
This option sets compiler optimization level (gcc -O argument).
- for "Release" setting, -Os flag is added to CFLAGS.
- for "Debug" setting, -Og flag is added to CFLAGS.
"Release" with -Os produces smaller & faster compiled code but it
may be harder to correlated code addresses to source files when debugging.
To add custom optimization settings, set CFLAGS and/or CPPFLAGS
in project makefile, before including $(IDF_PATH)/make/project.mk. Note that
custom optimization levels may be unsupported.
config OPTIMIZATION_LEVEL_DEBUG
bool "Debug (-Og)"
config OPTIMIZATION_LEVEL_RELEASE
bool "Release (-Os)"
endchoice
choice OPTIMIZATION_ASSERTION_LEVEL
prompt "Assertion level"
default OPTIMIZATION_ASSERTIONS_ENABLED
help
Assertions can be:
- Enabled. Failure will print verbose assertion details. This is the default.
- Set to "silent" to save code size (failed assertions will abort() but user
needs to use the aborting address to find the line number with the failed assertion.)
- Disabled entirely (not recommended for most configurations.) -DNDEBUG is added
to CPPFLAGS in this case.
config OPTIMIZATION_ASSERTIONS_ENABLED
prompt "Enabled"
bool
help
Enable assertions. Assertion content and line number will be printed on failure.
config OPTIMIZATION_ASSERTIONS_SILENT
prompt "Silent (saves code size)"
bool
help
Enable silent assertions. Failed assertions will abort(), user needs to
use the aborting address to find the line number with the failed assertion.
config OPTIMIZATION_ASSERTIONS_DISABLED
prompt "Disabled (sets -DNDEBUG)"
bool
help
If assertions are disabled, -DNDEBUG is added to CPPFLAGS.
endchoice # assertions
menuconfig CXX_EXCEPTIONS
bool "Enable C++ exceptions"
default n
help
Enabling this option compiles all IDF C++ files with exception support enabled.
Disabling this option disables C++ exception support in all compiled files, and any libstdc++ code which throws
an exception will abort instead.
Enabling this option currently adds an additional ~500 bytes of heap overhead
when an exception is thrown in user code for the first time.
config CXX_EXCEPTIONS_EMG_POOL_SIZE
int "Emergency Pool Size"
default 0
depends on CXX_EXCEPTIONS
help
Size (in bytes) of the emergency memory pool for C++ exceptions. This pool will be used to allocate
memory for thrown exceptions when there is not enough memory on the heap.
choice STACK_CHECK_MODE
prompt "Stack smashing protection mode"
default STACK_CHECK_NONE
help
Stack smashing protection mode. Emit extra code to check for buffer overflows, such as stack
smashing attacks. This is done by adding a guard variable to functions with vulnerable objects.
The guards are initialized when a function is entered and then checked when the function exits.
If a guard check fails, program is halted. Protection has the following modes:
- In NORMAL mode (GCC flag: -fstack-protector) only functions that call alloca,
and functions with buffers larger than 8 bytes are protected.
- STRONG mode (GCC flag: -fstack-protector-strong) is like NORMAL, but includes
additional functions to be protected -- those that have local array definitions,
or have references to local frame addresses.
- In OVERALL mode (GCC flag: -fstack-protector-all) all functions are protected.
Modes have the following impact on code performance and coverage:
- performance: NORMAL > STRONG > OVERALL
- coverage: NORMAL < STRONG < OVERALL
config STACK_CHECK_NONE
bool "None"
config STACK_CHECK_NORM
bool "Normal"
config STACK_CHECK_STRONG
bool "Strong"
config STACK_CHECK_ALL
bool "Overall"
endchoice
config STACK_CHECK
bool
default !STACK_CHECK_NONE
help
Stack smashing protection.
config WARN_WRITE_STRINGS
bool "Enable -Wwrite-strings warning flag"
default "n"
help
Adds -Wwrite-strings flag for the C/C++ compilers.
For C, this gives string constants the type ``const char[]`` so that
copying the address of one into a non-const ``char *`` pointer
produces a warning. This warning helps to find at compile time code
that tries to write into a string constant.
For C++, this warns about the deprecated conversion from string
literals to ``char *``.
endmenu # Compiler Options

View file

@ -55,43 +55,43 @@ typedef struct {
static const esp_err_msg_t esp_err_msg_table[] = {
// components/esp32/include/esp_err.h
# ifdef ESP_FAIL
ERR_TBL_IT(ESP_FAIL), /* -1 */
ERR_TBL_IT(ESP_FAIL), /* -1 Generic esp_err_t code indicating failure */
# endif
# ifdef ESP_OK
ERR_TBL_IT(ESP_OK), /* 0 */
ERR_TBL_IT(ESP_OK), /* 0 esp_err_t value indicating success (no error) */
# endif
# ifdef ESP_ERR_NO_MEM
ERR_TBL_IT(ESP_ERR_NO_MEM), /* 257 0x101 */
ERR_TBL_IT(ESP_ERR_NO_MEM), /* 257 0x101 Out of memory */
# endif
# ifdef ESP_ERR_INVALID_ARG
ERR_TBL_IT(ESP_ERR_INVALID_ARG), /* 258 0x102 */
ERR_TBL_IT(ESP_ERR_INVALID_ARG), /* 258 0x102 Invalid argument */
# endif
# ifdef ESP_ERR_INVALID_STATE
ERR_TBL_IT(ESP_ERR_INVALID_STATE), /* 259 0x103 */
ERR_TBL_IT(ESP_ERR_INVALID_STATE), /* 259 0x103 Invalid state */
# endif
# ifdef ESP_ERR_INVALID_SIZE
ERR_TBL_IT(ESP_ERR_INVALID_SIZE), /* 260 0x104 */
ERR_TBL_IT(ESP_ERR_INVALID_SIZE), /* 260 0x104 Invalid size */
# endif
# ifdef ESP_ERR_NOT_FOUND
ERR_TBL_IT(ESP_ERR_NOT_FOUND), /* 261 0x105 */
ERR_TBL_IT(ESP_ERR_NOT_FOUND), /* 261 0x105 Requested resource not found */
# endif
# ifdef ESP_ERR_NOT_SUPPORTED
ERR_TBL_IT(ESP_ERR_NOT_SUPPORTED), /* 262 0x106 */
ERR_TBL_IT(ESP_ERR_NOT_SUPPORTED), /* 262 0x106 Operation or feature not supported */
# endif
# ifdef ESP_ERR_TIMEOUT
ERR_TBL_IT(ESP_ERR_TIMEOUT), /* 263 0x107 */
ERR_TBL_IT(ESP_ERR_TIMEOUT), /* 263 0x107 Operation timed out */
# endif
# ifdef ESP_ERR_INVALID_RESPONSE
ERR_TBL_IT(ESP_ERR_INVALID_RESPONSE), /* 264 0x108 */
ERR_TBL_IT(ESP_ERR_INVALID_RESPONSE), /* 264 0x108 Received response was invalid */
# endif
# ifdef ESP_ERR_INVALID_CRC
ERR_TBL_IT(ESP_ERR_INVALID_CRC), /* 265 0x109 */
ERR_TBL_IT(ESP_ERR_INVALID_CRC), /* 265 0x109 CRC or checksum was invalid */
# endif
# ifdef ESP_ERR_INVALID_VERSION
ERR_TBL_IT(ESP_ERR_INVALID_VERSION), /* 266 0x10a */
ERR_TBL_IT(ESP_ERR_INVALID_VERSION), /* 266 0x10a Version was invalid */
# endif
# ifdef ESP_ERR_INVALID_MAC
ERR_TBL_IT(ESP_ERR_INVALID_MAC), /* 267 0x10b */
ERR_TBL_IT(ESP_ERR_INVALID_MAC), /* 267 0x10b MAC address was invalid */
# endif
// components/nvs_flash/include/nvs.h
# ifdef ESP_ERR_NVS_BASE
@ -132,7 +132,7 @@ static const esp_err_msg_t esp_err_msg_table[] = {
ERR_TBL_IT(ESP_ERR_NVS_KEY_TOO_LONG), /* 4361 0x1109 Key name is too long */
# endif
# ifdef ESP_ERR_NVS_PAGE_FULL
ERR_TBL_IT(ESP_ERR_NVS_PAGE_FULL), /* 4362 0x110a Internal error; never returned by nvs_ API
ERR_TBL_IT(ESP_ERR_NVS_PAGE_FULL), /* 4362 0x110a Internal error; never returned by nvs API
functions */
# endif
# ifdef ESP_ERR_NVS_INVALID_STATE
@ -473,7 +473,7 @@ const char *esp_err_to_name_r(esp_err_t code, char *buf, size_t buflen)
return buf;
}
snprintf(buf, buflen, "Unknown error %d", code);
snprintf(buf, buflen, "%s 0x%x(%d)", esp_unknown_msg, code, code);
return buf;
}

View file

@ -58,7 +58,7 @@ const char *esp_err_to_name_r(esp_err_t code, char *buf, size_t buflen)
return buf;
}
snprintf(buf, buflen, "Unknown error %d", code);
snprintf(buf, buflen, "%s 0x%x(%d)", esp_unknown_msg, code, code);
return buf;
}

View file

@ -24,21 +24,20 @@ extern "C" {
typedef int32_t esp_err_t;
/* Definitions for error constants. */
#define ESP_OK 0 /*!< esp_err_t value indicating success (no error) */
#define ESP_FAIL -1 /*!< Generic esp_err_t code indicating failure */
#define ESP_OK 0
#define ESP_FAIL -1
#define ESP_ERR_NO_MEM 0x101
#define ESP_ERR_INVALID_ARG 0x102
#define ESP_ERR_INVALID_STATE 0x103
#define ESP_ERR_INVALID_SIZE 0x104
#define ESP_ERR_NOT_FOUND 0x105
#define ESP_ERR_NOT_SUPPORTED 0x106
#define ESP_ERR_TIMEOUT 0x107
#define ESP_ERR_INVALID_RESPONSE 0x108
#define ESP_ERR_INVALID_CRC 0x109
#define ESP_ERR_INVALID_VERSION 0x10A
#define ESP_ERR_INVALID_MAC 0x10B
#define ESP_ERR_NO_MEM 0x101 /*!< Out of memory */
#define ESP_ERR_INVALID_ARG 0x102 /*!< Invalid argument */
#define ESP_ERR_INVALID_STATE 0x103 /*!< Invalid state */
#define ESP_ERR_INVALID_SIZE 0x104 /*!< Invalid size */
#define ESP_ERR_NOT_FOUND 0x105 /*!< Requested resource not found */
#define ESP_ERR_NOT_SUPPORTED 0x106 /*!< Operation or feature not supported */
#define ESP_ERR_TIMEOUT 0x107 /*!< Operation timed out */
#define ESP_ERR_INVALID_RESPONSE 0x108 /*!< Received response was invalid */
#define ESP_ERR_INVALID_CRC 0x109 /*!< CRC or checksum was invalid */
#define ESP_ERR_INVALID_VERSION 0x10A /*!< Version was invalid */
#define ESP_ERR_INVALID_MAC 0x10B /*!< MAC address was invalid */
#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */
#define ESP_ERR_MESH_BASE 0x4000 /*!< Starting number of MESH error codes */
@ -76,6 +75,7 @@ const char *esp_err_to_name(esp_err_t code);
*/
const char *esp_err_to_name_r(esp_err_t code, char *buf, size_t buflen);
/** @cond */
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression) __attribute__((noreturn));
#ifndef __ASSERT_FUNC
@ -88,6 +88,7 @@ void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const cha
#define __ASSERT_FUNC "??"
#endif
#endif
/** @endcond */
/**
* Macro which can be used to check the error code,

View file

@ -313,7 +313,7 @@ void xt_unhandled_exception(XtExcFrame *frame)
} else {
panicPutStr("Unknown");
}
panicPutStr(")\r\n");
panicPutStr(")");
if (esp_cpu_in_ocd_debug_mode()) {
panicPutStr(" at pc=");
panicPutHex(frame->pc);

View file

@ -38,7 +38,7 @@ typedef uint32_t nvs_handle;
#define ESP_ERR_NVS_INVALID_HANDLE (ESP_ERR_NVS_BASE + 0x07) /*!< Handle has been closed or is NULL */
#define ESP_ERR_NVS_REMOVE_FAILED (ESP_ERR_NVS_BASE + 0x08) /*!< The value wasnt updated because flash write operation has failed. The value was written however, and update will be finished after re-initialization of nvs, provided that flash operation doesnt fail again. */
#define ESP_ERR_NVS_KEY_TOO_LONG (ESP_ERR_NVS_BASE + 0x09) /*!< Key name is too long */
#define ESP_ERR_NVS_PAGE_FULL (ESP_ERR_NVS_BASE + 0x0a) /*!< Internal error; never returned by nvs_ API functions */
#define ESP_ERR_NVS_PAGE_FULL (ESP_ERR_NVS_BASE + 0x0a) /*!< Internal error; never returned by nvs API functions */
#define ESP_ERR_NVS_INVALID_STATE (ESP_ERR_NVS_BASE + 0x0b) /*!< NVS is in an inconsistent state due to a previous error. Call nvs_flash_init and nvs_open again, then retry. */
#define ESP_ERR_NVS_INVALID_LENGTH (ESP_ERR_NVS_BASE + 0x0c) /*!< String or blob length is not sufficient to store data */
#define ESP_ERR_NVS_NO_FREE_PAGES (ESP_ERR_NVS_BASE + 0x0d) /*!< NVS partition doesn't contain any empty pages. This may happen if NVS partition was truncated. Erase the whole partition and call nvs_flash_init again. */

View file

@ -174,7 +174,9 @@ INPUT = \
../../components/freertos/include/freertos/semphr.h \
../../components/freertos/include/freertos/timers.h \
../../components/freertos/include/freertos/event_groups.h \
../../components/freertos/include/freertos/ringbuf.h
../../components/freertos/include/freertos/ringbuf.h \
### Helper functions for error codes
../../components/esp32/include/esp_err.h

View file

@ -45,6 +45,11 @@ kconfig_inc_path = '{}/inc/kconfig.inc'.format(builddir)
os.system('python ../gen-kconfig-doc.py > ' + kconfig_inc_path + '.in')
copy_if_modified(kconfig_inc_path + '.in', kconfig_inc_path)
# Generate 'esp_err_defs.inc' file with ESP_ERR_ error code definitions
esp_err_inc_path = '{}/inc/esp_err_defs.inc'.format(builddir)
os.system('python ../../tools/gen_esp_err_to_name.py --rst_output ' + esp_err_inc_path + '.in')
copy_if_modified(esp_err_inc_path + '.in', esp_err_inc_path)
# http://stackoverflow.com/questions/12772927/specifying-an-online-image-in-sphinx-restructuredtext-format
#
suppress_warnings = ['image.nonlocal_uri']

View file

@ -0,0 +1,122 @@
.. highlight:: c
Error Handling
==============
Overview
--------
Identifying and handling run-time errors is important for developing robust applications. There can be multiple kinds of run-time errors:
- Recoverable errors:
- Errors indicated by functions through return values (error codes)
- C++ exceptions, thrown using ``throw`` keyword
- Unrecoverable (fatal) errors:
- Failed assertions (using ``assert`` macro and equivalent methods) and ``abort()`` calls.
- CPU exceptions: access to protected regions of memory, illegal instruction, etc.
- System level checks: watchdog timeout, cache access error, stack overflow, stack smashing, heap corruption, etc.
This guide explains ESP-IDF error handling mechanisms related to recoverable errors, and provides some common error handling patterns.
For instructions on diagnosing unrecoverable errors, see :doc:`Fatal Errors <fatal-errors>`.
Error codes
-----------
The majority of ESP-IDF-specific functions use :cpp:type:`esp_err_t` type to return error codes. :cpp:type:`esp_err_t` is a signed integer type. Success (no error) is indicated with ``ESP_OK`` code, which is defined as zero.
Various ESP-IDF header files define possible error codes using preprocessor defines. Usually these defines start with ``ESP_ERR_`` prefix. Common error codes for generic failures (out of memory, timeout, invalid argument, etc.) are defined in ``esp_err.h`` file. Various components in ESP-IDF may define additional error codes for specific situations.
For the complete list of error codes, see :doc:`Error Code Reference <../api-reference/error-codes>`.
Converting error codes to error messages
----------------------------------------
For each error code defined in ESP-IDF components, :cpp:type:`esp_err_t` value can be converted to an error code name using :cpp:func:`esp_err_to_name` or :cpp:func:`esp_err_to_name_r` functions. For example, passing ``0x101`` to :cpp:func:`esp_err_to_name` will return "ESP_ERR_NO_MEM" string. Such strings can be used in log output to make it easier to understand which error has happened.
Additionally, :cpp:func:`esp_err_to_name_r` function will attempt to interpret the error code as a `standard POSIX error code <http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html>`_, if no matching ``ESP_ERR_`` value is found. This is done using ``strerror_r`` function. POSIX error codes (such as ``ENOENT``, ``ENOMEM``) are defined in ``errno.h`` and are typically obtained from ``errno`` variable. In ESP-IDF this variable is thread-local: multiple FreeRTOS tasks have their own copies of ``errno``. Functions which set ``errno`` only modify its value for the task they run in.
This feature is enabled by default, but can be disabled to reduce application binary size. See :envvar:`CONFIG_ESP_ERR_TO_NAME_LOOKUP`. When this feature is disabled, :cpp:func:`esp_err_to_name` and :cpp:func:`esp_err_to_name_r` are still defined and can be called. In this case, :cpp:func:`esp_err_to_name` will return ``UNKNOWN ERROR``, and :cpp:func:`esp_err_to_name_r` will return ``Unknown error 0xXXXX(YYYYY)``, where ``0xXXXX`` and ``YYYYY`` are the hexadecimal and decimal representations of the error code, respectively.
``ESP_ERROR_CHECK`` macro
-------------------------
:cpp:func:`ESP_ERROR_CHECK` macro serves similar purpose as ``assert``, except that it checks :cpp:type:`esp_err_t` value rather than a ``bool`` condition. If the argument of :cpp:func:`ESP_ERROR_CHECK` is not equal :c:macro:`ESP_OK`, then an error message is printed on the console, and ``abort()`` is called.
Error message will typically look like this::
ESP_ERROR_CHECK failed: esp_err_t 0x107 (ESP_ERR_TIMEOUT) at 0x400d1fdf
file: "/Users/user/esp/example/main/main.c" line 20
func: app_main
expression: sdmmc_card_init(host, &card)
Backtrace: 0x40086e7c:0x3ffb4ff0 0x40087328:0x3ffb5010 0x400d1fdf:0x3ffb5030 0x400d0816:0x3ffb5050
.. note:: If :doc:`IDF monitor <../get-started/idf-monitor>` is used, addresses in the backtrace will be converted to file names and line numbers.
- The first line mentions the error code as a hexadecimal value, and the identifier used for this error in source code. The latter depends on :envvar:`CONFIG_ESP_ERR_TO_NAME_LOOKUP` option being set. Address in the program where error has occured is printed as well.
- Subsequent lines show the location in the program where :cpp:func:`ESP_ERROR_CHECK` macro was called, and the expression which was passed to the macro as an argument.
- Finally, backtrace is printed. This is part of panic handler output common to all fatal errors. See :doc:`Fatal Errors <fatal-errors>` for more information about the backtrace.
Error handling patterns
-----------------------
1. Attempt to recover. Depending on the situation, this might mean to retry the call after some time, or attempt to de-initialize the driver and re-initialize it again, or fix the error condition using an out-of-band mechanism (e.g reset an external peripheral which is not responding).
Example::
esp_err_t err;
do {
err = sdio_slave_send_queue(addr, len, arg, timeout);
// keep retrying while the sending queue is full
} while (err == ESP_ERR_TIMEOUT);
if (err != ESP_OK) {
// handle other errors
}
2. Propagate the error to the caller. In some middleware components this means that a function must exit with the same error code, making sure any resource allocations are rolled back.
Example::
sdmmc_card_t* card = calloc(1, sizeof(sdmmc_card_t));
if (card == NULL) {
return ESP_ERR_NO_MEM;
}
esp_err_t err = sdmmc_card_init(host, &card);
if (err != ESP_OK) {
// Clean up
free(card);
// Propagate the error to the upper layer (e.g. to notify the user).
// Alternatively, application can define and return custom error code.
return err;
}
3. Convert into unrecoverable error, for example using ``ESP_ERROR_CHECK``. See `ESP_ERROR_CHECK macro`_ section for details.
Terminating the application in case of an error is usually undesirable behaviour for middleware components, but is sometimes acceptable at application level.
Many ESP-IDF examples use ``ESP_ERROR_CHECK`` to handle errors from various APIs. This is not the best practice for applications, and is done to make example code more concise.
Example::
ESP_ERROR_CHECK(spi_bus_initialize(host, bus_config, dma_chan));
C++ Exceptions
--------------
Support for C++ Exceptions in ESP-IDF is disabled by default, but can be enabled using :envvar:`CONFIG_CXX_EXCEPTIONS` option.
Enabling exception handling normally increases application binary size by a few kB. Additionally it may be necessary to reserve some amount of RAM for exception emergency pool. Memory from this pool will be used if it is not possible to allocate exception object from the heap. Amount of memory in the emergency pool can be set using :envvar:`CONFIG_CXX_EXCEPTIONS_EMG_POOL_SIZE` variable.
If an exception is thrown, but there is no ``catch`` block, the program will be terminated by ``abort`` function, and backtrace will be printed. See :doc:`Fatal Errors <fatal-errors>` for more information about backtraces.

View file

@ -0,0 +1,293 @@
Fatal Errors
============
Overview
--------
In certain situations, execution of the program can not be continued in a well defined way. In ESP-IDF, these situations include:
- CPU Exceptions: Illegal Instruction, Load/Store Alignment Error, Load/Store Prohibited error, Double Exception.
- System level checks and safeguards:
- :doc:`Interrupt watchdog <../api-reference/system/wdts>` timeout
- :doc:`Task watchdog <../api-reference/system/wdts>` timeout (only fatal if :envvar:`CONFIG_TASK_WDT_PANIC` is set)
- Cache access error
- Brownout detection event
- Stack overflow
- Stack smashing protection check
- Heap integrity check
- Failed assertions, via ``assert``, ``configASSERT`` and similar macros.
This guide explains the procedure used in ESP-IDF for handling these errors, and provides suggestions on troubleshooting the errors.
Panic Handler
-------------
Every error cause listed in the `Overview`_ will be handled by *panic handler*.
Panic handler will start by printing the cause of the error to the console. For CPU exceptions, the message will be similar to::
Guru Meditation Error: Core 0 panic'ed (IllegalInstruction). Exception was unhandled.
For some of the system level checks (interrupt watchdog, cache access error), the message will be similar to::
Guru Meditation Error: Core 0 panic'ed (Cache disabled but cached memory region accessed)
In all cases, error cause will be printed in parens. See `Guru Meditation Errors`_ for a list of possible error causes.
Subsequent behavior of the panic handler can be set using :envvar:`CONFIG_ESP32_PANIC` configuration choice. The available options are:
- Print registers and reboot (``CONFIG_ESP32_PANIC_PRINT_REBOOT``) — default option.
This will print register values at the point of the exception, print the backtrace, and restart the chip.
- Print registers and halt (``CONFIG_ESP32_PANIC_PRINT_HALT``)
Similar to the above option, but halt instead of rebooting. External reset is required to restart the program.
- Silent reboot (``CONFIG_ESP32_PANIC_SILENT_REBOOT``)
Don't print registers or backtrace, restart the chip immediately.
- Invoke GDB Stub (``CONFIG_ESP32_PANIC_GDBSTUB``)
Start GDB server which can communicate with GDB over console UART port. See `GDB Stub`_ for more details.
Behavior of panic handler is affected by two other configuration options.
- If :envvar:`CONFIG_ESP32_DEBUG_OCDAWARE` is enabled (which is the default), panic handler will detect whether a JTAG debugger is connected. If it is, execution will be halted and control will be passed to the debugger. In this case registers and backtrace are not dumped to the console, and GDBStub / Core Dump functions are not used.
- If :doc:`Core Dump <core_dump>` feature is enabled (``CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH`` or ``CONFIG_ESP32_ENABLE_COREDUMP_TO_UART`` options), then system state (task stacks and registers) will be dumped either to Flash or UART, for later analysis.
The following diagram illustrates panic handler behavior:
.. blockdiag::
:caption: Panic Handler Flowchart
:align: center
blockdiag panic-handler {
orientation = portrait;
edge_layout = flowchart;
default_group_color = white;
cpu_exception [label = "CPU Exception", shape=roundedbox];
sys_check [label = "Cache error,\nInterrupt WDT,\nabort()", shape=roundedbox];
check_ocd [label = "JTAG debugger\nconnected?", shape=diamond, width=160, height=80];
print_error_cause [label = "Print error/\nexception cause"];
use_jtag [label = "Send signal to\nJTAG debugger", shape=roundedbox];
dump_registers [label = "Print registers\nand backtrace"];
check_coredump [label = "Core dump\nenabled?", shape=flowchart.condition];
do_coredump [label = "Core dump\nto UART or Flash"];
check_gdbstub [label = "GDB Stub\nenabled?", shape=flowchart.condition];
do_gdbstub [label = "Start GDB Stub", shape=roundedbox];
halt [label = "Halt", shape=roundedbox];
reboot [label = "Reboot", shape=roundedbox];
check_halt [label = "Halt?", shape=flowchart.condition];
group {cpu_exception, sys_check};
cpu_exception -> print_error_cause;
sys_check -> print_error_cause;
print_error_cause -> check_ocd;
check_ocd -> use_jtag [label = "Yes"];
check_ocd -> dump_registers [label = "No"];
dump_registers -> check_coredump
check_coredump -> do_coredump [label = "Yes"];
do_coredump -> check_gdbstub;
check_coredump -> check_gdbstub [label = "No"];
check_gdbstub -> check_halt [label = "No"];
check_gdbstub -> do_gdbstub [label = "Yes"];
check_halt -> halt [label = "Yes"];
check_halt -> reboot [label = "No"];
}
Register Dump and Backtrace
---------------------------
Unless ``CONFIG_ESP32_PANIC_SILENT_REBOOT`` option is enabled, panic handler prints some of the CPU registers, and the backtrace, to the console::
Core 0 register dump:
PC : 0x400e14ed PS : 0x00060030 A0 : 0x800d0805 A1 : 0x3ffb5030
A2 : 0x00000000 A3 : 0x00000001 A4 : 0x00000001 A5 : 0x3ffb50dc
A6 : 0x00000000 A7 : 0x00000001 A8 : 0x00000000 A9 : 0x3ffb5000
A10 : 0x00000000 A11 : 0x3ffb2bac A12 : 0x40082d1c A13 : 0x06ff1ff8
A14 : 0x3ffb7078 A15 : 0x00000000 SAR : 0x00000014 EXCCAUSE: 0x0000001d
EXCVADDR: 0x00000000 LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0xffffffff
Backtrace: 0x400e14ed:0x3ffb5030 0x400d0802:0x3ffb5050
Register values printed are the register values in the exception frame, i.e. values at the moment when CPU exception or other fatal error has occured.
Register dump is not printed if the panic handler was executed as a result of an ``abort()`` call.
In some cases, such as interrupt watchdog timeout, panic handler may print additional CPU registers (EPC1-EPC4) and the registers/backtrace of the code running on the other CPU.
Backtrace line contains PC:SP pairs, where PC is the Program Counter and SP is Stack Pointer, for each stack frame of the current task. If a fatal error happens inside an ISR, the backtrace may include PC:SP pairs both from the task which was interrupted, and from the ISR.
If :doc:`IDF Monitor <../get-started/idf-monitor>` is used, Program Counter values will be converted to code locations (function name, file name, and line number), and the output will be annotated with additional lines::
Core 0 register dump:
PC : 0x400e14ed PS : 0x00060030 A0 : 0x800d0805 A1 : 0x3ffb5030
0x400e14ed: app_main at /Users/user/esp/example/main/main.cpp:36
A2 : 0x00000000 A3 : 0x00000001 A4 : 0x00000001 A5 : 0x3ffb50dc
A6 : 0x00000000 A7 : 0x00000001 A8 : 0x00000000 A9 : 0x3ffb5000
A10 : 0x00000000 A11 : 0x3ffb2bac A12 : 0x40082d1c A13 : 0x06ff1ff8
0x40082d1c: _calloc_r at /Users/user/esp/esp-idf/components/newlib/syscalls.c:51
A14 : 0x3ffb7078 A15 : 0x00000000 SAR : 0x00000014 EXCCAUSE: 0x0000001d
EXCVADDR: 0x00000000 LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0xffffffff
Backtrace: 0x400e14ed:0x3ffb5030 0x400d0802:0x3ffb5050
0x400e14ed: app_main at /Users/user/esp/example/main/main.cpp:36
0x400d0802: main_task at /Users/user/esp/esp-idf/components/esp32/cpu_start.c:470
To find the location where a fatal error has happened, look at the lines which follow the "Backtrace" line. Fatal error location is the top line, and subsequent lines show the call stack.
GDB Stub
--------
If ``CONFIG_ESP32_PANIC_GDBSTUB`` option is enabled, panic handler will not reset the chip when fatal error happens. Instead, it will start GDB remote protocol server, commonly referred to as GDB Stub. When this happens, GDB instance running on the host computer can be instructed to connect to the ESP32 UART port.
If :doc:`IDF Monitor <../get-started/idf-monitor>` is used, GDB is started automatically when GDB Stub prompt is detected on the UART. The output would look like this::
Entering gdb stub now.
$T0b#e6GNU gdb (crosstool-NG crosstool-ng-1.22.0-80-gff1f415) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-build_apple-darwin16.3.0 --target=xtensa-esp32-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /Users/user/esp/example/build/example.elf...done.
Remote debugging using /dev/cu.usbserial-31301
0x400e1b41 in app_main ()
at /Users/user/esp/example/main/main.cpp:36
36 *((int*) 0) = 0;
(gdb)
GDB prompt can be used to inspect CPU registers, local and static variables, and arbitrary locations in memory. It is not possible to set breakpoints, change PC, or continue execution. To reset the program, exit GDB and perform external reset: Ctrl-T Ctrl-R in IDF Monitor, or using external reset button on the development board.
Guru Meditation Errors
----------------------
.. Note to editor: titles of the following section need to match exception causes printed by the panic handler. Do not change the titles (insert spaces, reword, etc.) unless panic handler messages are also changed.
.. Note to translator: When translating this section, avoid translating the following section titles. "Guru Meditation" in the title of this section should also not be translated. Keep these two notes when translating.
This section explains the meaning of different error causes, printed in parens after ``Guru Meditation Error: Core panic'ed`` message.
.. note:: See `Wikipedia article <https://en.wikipedia.org/wiki/Guru_Meditation>`_ for historical origins of "Guru Meditation".
IllegalInstruction
^^^^^^^^^^^^^^^^^^
This CPU exception indicates that the instruction which was executed was not a valid instruction.
Most common reasons for this error include:
- FreeRTOS task function has returned. In FreeRTOS, if task function needs to terminate, it should call :cpp:func:`vTaskDelete` function and delete itself, instead of returning.
- Failure to load next instruction from SPI flash. This usually happens if:
- Application has reconfigured SPI flash pins as some other function (GPIO, UART, etc.). Consult Hardware Design Guidelines and the Datasheet for the chip or module for details about SPI flash pins.
- Some external device was accidentally connected to SPI flash pins, and has interfered with communication between ESP32 and SPI flash.
InstrFetchProhibited
^^^^^^^^^^^^^^^^^^^^
This CPU exception indicates that CPU could not load an instruction because the the address of the instruction did not belong to a valid region in instruction RAM or ROM.
Usually this means an attempt to call a function pointer, which does not point to valid code. ``PC`` (Program Counter) register can be used as an indicator: it will be zero or will contain garbage value (not ``0x4xxxxxxx``).
LoadProhibited, StoreProhibited
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This CPU exception happens when application attempts to read from or write to an invalid memory location. The address which was written/read is found in ``EXCVADDR`` register in the register dump. If this address is zero, it usually means that application attempted to dereference a NULL pointer. If this address is close to zero, it usually means that application attempted to access member of a structure, but the pointer to the structure was NULL. If this address is something else (garbage value, not in ``0x3fxxxxxx`` - ``0x6xxxxxxx`` range), it likely means that the pointer used to access the data was either not initialized or was corrupted.
IntegerDivideByZero
^^^^^^^^^^^^^^^^^^^
Application has attempted to do integer division by zero.
LoadStoreAlignment
^^^^^^^^^^^^^^^^^^
Application has attempted to read or write memory location, and address alignment did not match load/store size. For example, 32-bit load can only be done from 4-byte aligned address, and 16-bit load can only be done from a 2-byte aligned address.
LoadStoreError
^^^^^^^^^^^^^^
Application has attempted to do a 8- or 16- bit load/store from a memory region which only supports 32-bit loads/stores. For example, dereferencing a ``char*`` pointer which points into intruction memory will result in such an error.
Unhandled debug exception
^^^^^^^^^^^^^^^^^^^^^^^^^
This will usually be followed by a message like::
Debug exception reason: Stack canary watchpoint triggered (task_name)
This error indicates that application has written past the end of the stack of ``task_name`` task. Note that not every stack overflow is guaranteed to trigger this error. It is possible that the task writes to stack beyond the stack canary location, in which case the watchpoint will not be triggered.
Interrupt wdt timeout on CPU0 / CPU1
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Indicates that interrupt watchdog timeout has occured. See :doc:`Watchdogs <../api-reference/system/wdts>` for more information.
Cache disabled but cached memory region accessed
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In some situations ESP-IDF will temporarily disable access to external SPI Flash and SPI RAM via caches. For example, this happens with spi_flash APIs are used to read/write/erase/mmap regions of SPI Flash. In these situations, tasks are suspended, and interrupt handlers not registered with ``ESP_INTR_FLAG_IRAM`` are disabled. Make sure that any interrupt handlers registered with this flag have all the code and data in IRAM/DRAM. Refer to the :ref:`SPI flash API documentation <iram-safe-interrupt-handlers>` for more details.
Other Fatal Errors
------------------
Brownout
^^^^^^^^
ESP32 has a built-in brownout detector, which is enabled by default. Brownout detector can trigger system reset if supply voltage goes below safe level. Brownout detector can be configured using :envvar:`CONFIG_BROWNOUT_DET` and :envvar:`CONFIG_BROWNOUT_DET_LVL_SEL` options.
When brownout detector triggers, the following message is printed::
Brownout detector was triggered
Chip is reset after the message is printed.
Note that if supply voltage is dropping at a fast rate, only part of the message may be seen on the console.
Corrupt Heap
^^^^^^^^^^^^
ESP-IDF heap implementation contains a number of run-time checks of heap structure. Additional checks ("Heap Poisoning") can be enabled in menuconfig. If one of the checks fails, message similar to the following will be printed::
CORRUPT HEAP: Bad tail at 0x3ffe270a. Expected 0xbaad5678 got 0xbaac5678
assertion "head != NULL" failed: file "/Users/user/esp/esp-idf/components/heap/multi_heap_poisoning.c", line 201, function: multi_heap_free
abort() was called at PC 0x400dca43 on core 0
Consult :doc:`Heap Memory Debugging <../api-reference/system/heap_debug>` documentation for further information.
Stack Smashing
^^^^^^^^^^^^^^
Stack smashing protection (based on GCC ``-fstack-protector*`` flags) can be enabled in ESP-IDF using :envvar:`CONFIG_STACK_CHECK_MODE` option. If stack smashing is detected, message similar to the following will be printed::
Stack smashing protect failure!
abort() was called at PC 0x400d2138 on core 0
Backtrace: 0x4008e6c0:0x3ffc1780 0x4008e8b7:0x3ffc17a0 0x400d2138:0x3ffc17c0 0x400e79d5:0x3ffc17e0 0x400e79a7:0x3ffc1840 0x400e79df:0x3ffc18a0 0x400e2235:0x3ffc18c0 0x400e1916:0x3ffc18f0 0x400e19cd:0x3ffc1910 0x400e1a11:0x3ffc1930 0x400e1bb2:0x3ffc1950 0x400d2c44:0x3ffc1a80
0
The backtrace should point to the function where stack smashing has occured. Check the function code for unbounded access to local arrays.

View file

@ -6,6 +6,8 @@ API Guides
General Notes <general-notes>
Build System <build-system>
Error Handling <error-handling>
Fatal Errors <fatal-errors>
Deep Sleep Wake Stubs <deep-sleep-stub>
ESP32 Core Dump <core_dump>
Flash Encryption <../security/flash-encryption>

View file

@ -0,0 +1,8 @@
Error Codes Reference
=====================
This section lists various error code constants defined in ESP-IDF.
For general information about error codes in ESP-IDF, see :doc:`Error Handling <../api-guides/error-handling>`.
.. include:: /_build/inc/esp_err_defs.inc

View file

@ -14,4 +14,5 @@ API Reference
Storage <storage/index>
System <system/index>
Configuration Options <kconfig>
Error Codes Reference <error-codes>

View file

@ -0,0 +1,14 @@
Error Codes and Helper Functions
================================
This section lists definitions of common ESP-IDF error codes and several helper functions related to error handling.
For general information about error codes in ESP-IDF, see :doc:`Error Handling <../../api-guides/error-handling>`.
For the full list of error codes defined in ESP-IDF, see :doc:`Error Code Reference <../error-codes>`.
API Reference
-------------
.. include:: /_build/inc/esp_err.inc

View file

@ -19,6 +19,7 @@ System API
Base MAC address <base_mac_address>
Over The Air Updates (OTA) <ota>
ESP pthread <esp_pthread>
Error Codes and Helper Functions <esp_err>
Example code for this API section is provided in :example:`system` directory of ESP-IDF examples.

View file

@ -122,6 +122,9 @@ def print_all_components():
process_kconfig_file(kconfig_file_path, heading_level, 'Component config')
process_kconfig_file(kconfig_file_path + '.projbuild', heading_level, '')
kconfig_file_path = os.path.join(os.path.curdir, '../..', 'Kconfig.compiler')
process_kconfig_file(kconfig_file_path, heading_level, '')
if __name__ == '__main__':
print_all_components()

View file

@ -0,0 +1 @@
.. include:: ../../en/api-guides/error-handling.rst

View file

@ -0,0 +1 @@
.. include:: ../../en/api-guides/fatal-errors.rst

View file

@ -6,6 +6,8 @@ API Guides
General Notes <general-notes>
Build System <build-system>
Error Handling <error-handling>
Fatal Errors <fatal-errors>
Deep Sleep Wake Stubs <deep-sleep-stub>
ESP32 Core Dump <core_dump>
Flash Encryption <../security/flash-encryption>

View file

@ -0,0 +1 @@
.. include:: ../../en/api-reference/error-codes.rst

View file

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/system/esp_err.rst

View file

@ -204,7 +204,7 @@ def max_string_width():
max = x
return max
def generate_output(fin, fout):
def generate_c_output(fin, fout):
"""
Writes the output to fout based on th error dictionary err_dict and
template file fin.
@ -264,10 +264,23 @@ def generate_output(fin, fout):
else:
fout.write(line)
def generate_rst_output(fout):
for k in sorted(err_dict.keys()):
v = err_dict[k][0]
fout.write(':c:macro:`{}` '.format(v.name))
if k > 0:
fout.write('**(0x{:x})**'.format(k))
else:
fout.write('({:d})'.format(k))
if len(v.comment) > 0:
fout.write(': {}'.format(v.comment))
fout.write('\n\n')
def main():
parser = argparse.ArgumentParser(description='ESP32 esp_err_to_name lookup generator for esp_err_t')
parser.add_argument('input', help='Path to the esp_err_to_name.c.in template input.', default=os.environ['IDF_PATH'] + '/components/esp32/esp_err_to_name.c.in', nargs='?')
parser.add_argument('output', help='Path to the esp_err_to_name.c output.', default=os.environ['IDF_PATH'] + '/components/esp32/esp_err_to_name.c', nargs='?')
parser.add_argument('--c_input', help='Path to the esp_err_to_name.c.in template input.', default=os.environ['IDF_PATH'] + '/components/esp32/esp_err_to_name.c.in')
parser.add_argument('--c_output', help='Path to the esp_err_to_name.c output.', default=os.environ['IDF_PATH'] + '/components/esp32/esp_err_to_name.c')
parser.add_argument('--rst_output', help='Generate .rst output and save it into this file')
args = parser.parse_args()
for root, dirnames, filenames in os.walk(os.environ['IDF_PATH']):
@ -287,8 +300,12 @@ def main():
process_remaining_errors()
with open(args.input, 'r') as fin, open(args.output, 'w') as fout:
generate_output(fin, fout)
if args.rst_output is not None:
with open(args.rst_output, 'w') as fout:
generate_rst_output(fout)
else:
with open(args.c_input, 'r') as fin, open(args.c_output, 'w') as fout:
generate_c_output(fin, fout)
if __name__ == "__main__":
main()