├── LICENSE ├── esp32-backtrace └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Thorsten von Eicken 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /esp32-backtrace: -------------------------------------------------------------------------------- 1 | #! /bin/bash -e 2 | # Hacked together by Thorsten von Eicken in 2019 3 | XTENSA_GDB=~/.platformio/packages/toolchain-xtensa32/bin/xtensa-esp32-elf-gdb 4 | 5 | # Validate commandline arguments 6 | if [ -z "$1" ]; then 7 | echo "usage: $0 []" 1>&2 8 | echo "reads from stdin if no backtrace-text is specified" 2>&2 9 | exit 1 10 | fi 11 | elf="$1" 12 | if ! [ -f $elf ]; then 13 | echo "ELF file not found ($elf)" 1>&2 14 | exit 1 15 | fi 16 | 17 | if [ -z "$2" ]; then 18 | echo "reading backtrace from stdin" 19 | bt=/dev/stdin 20 | elif ! [ -f "$2" ]; then 21 | echo "Backtrace file not found ($2)" 1>&2 22 | exit 1 23 | else 24 | bt=$2 25 | fi 26 | 27 | # Parse exception info and command backtrace 28 | rePC='PC\s*:? (0x40[0-2][0-9a-f]*)' 29 | reEA='EXCVADDR\s*: (0x40[0-2][0-9a-f]*)' 30 | reBT='Backtrace: (.*)' 31 | reIN='^[0-9a-f:x ]+$' 32 | reOT='[^0-9a-zA-Z](0x40[0-2][0-9a-f]{5})[^0-9a-zA-Z]' 33 | inBT=0 34 | declare -a REGS 35 | BT= 36 | while read p; do 37 | if [[ "$p" =~ $rePC ]]; then 38 | # PC : 0x400d38fe PS : 0x00060630 A0 : 0x800d35a6 A1 : 0x3ffb1c50 39 | REGS+=(PC:${BASH_REMATCH[1]}) 40 | elif [[ "$p" =~ $reEA ]]; then 41 | # EXCVADDR: 0x16000001 LBEG : 0x400014fd LEND : 0x4000150d LCOUNT : 0xffffffff 42 | REGS+=(EXCVADDR:${BASH_REMATCH[1]}) 43 | elif [[ "$p" =~ $reBT ]]; then 44 | # Backtrace: 0x400d38fe:0x3ffb1c50 0x400d35a3:0x3ffb1c90 0x400e40bf:0x3ffb1fb0 45 | BT="${BASH_REMATCH[1]}" 46 | inBT=1 47 | elif [[ $inBT ]]; then 48 | if [[ "$p" =~ $reIN ]]; then 49 | # backtrace continuation lines cut by terminal emulator 50 | # 0x400d38fe:0x3ffb1c50 0x400d35a3:0x3ffb1c90 51 | BT="$BT${BASH_REMATCH[0]}" 52 | else 53 | inBT=0 54 | fi 55 | elif [[ "$p" =~ $reOT ]]; then 56 | # other lines with addresses 57 | # A2 : 0x00000001 A3 : 0x00000000 A4 : 0x16000001 A5 : 0x31a07a28 58 | REGS+=(OTHER:${BASH_REMATCH[1]}) 59 | fi 60 | done <$bt 61 | 62 | #echo "PC is ${PC}" 63 | #echo "EA is ${EA}" 64 | #echo "BT is ${BT}" 65 | 66 | # Parse addresses in backtrace and add them to REGS 67 | n=0 68 | for stk in $BT; do 69 | # ex: 0x400d38fe:0x3ffb1c50 70 | if [[ $stk =~ (0x40[0-2][0-9a-f]+): ]]; then 71 | addr=${BASH_REMATCH[1]} 72 | REGS+=("BT-${n}:${addr}") 73 | fi 74 | let "n=$n + 1" 75 | #[[ $n -gt 10 ]] && break 76 | done 77 | 78 | # Iterate through all addresses and ask GDB to print source info for each one 79 | for reg in "${REGS[@]}"; do 80 | name=${reg%:*} 81 | addr=${reg#*:} 82 | #echo "Checking $name: $addr" 83 | info=$($XTENSA_GDB --batch $elf -ex "set listsize 1" -ex "l *$addr" -ex q 2>&1 |\ 84 | sed -e 's;/[^ ]*/ESP32/;;' |\ 85 | egrep -v "(No such file or directory)|(^$)") || true 86 | if [[ -n "$info" ]]; then echo "$name: $info"; fi 87 | done 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ESP32 Exception Stack Backtrace Analyzer 2 | ======================================== 3 | 4 | This little bash script parses the exception backtrace printed by the ESP32 firmware 5 | and uses GDB to print out the source location of all code addresses. 6 | 7 | It is heavily inspired by https://github.com/me-no-dev/EspExceptionDecoder but only uses 8 | bash and xtensa-esp32-elf GDB. 9 | 10 | __Install:__ Place the esp32-backtrace script somewhere in your path or use the full pathname to run 11 | it. If you are not using platformio to build your project you need to edit the `XTENSA_GDB` 12 | definition at the top of the script. 13 | 14 | Example 15 | ------- 16 | My program crashed printing: 17 | ``` 18 | Guru Meditation Error: Core 0 panic'ed (Unhandled debug exception) 19 | Debug exception reason: Stack canary watchpoint triggered (Tmr Svc) 20 | Core 0 register dump: 21 | PC : 0x40081708 PS : 0x00060336 A0 : 0x3ffbc990 A1 : 0x3ffbc8d0 22 | A2 : 0x3ffb7dd0 A3 : 0x00000000 A4 : 0x3ffb7e18 A5 : 0x00000000 23 | A6 : 0x00000001 A7 : 0x00000018 A8 : 0x800894ac A9 : 0x3ffbc970 24 | A10 : 0x00000000 A11 : 0x3ffc12c8 A12 : 0x3ffc12c8 A13 : 0x00000001 25 | A14 : 0x00060320 A15 : 0x00000000 SAR : 0x00000002 EXCCAUSE: 0x00000001 26 | EXCVADDR: 0x00000000 LBEG : 0x40001609 LEND : 0x4000160d LCOUNT : 0x00000000 27 | 28 | Backtrace: 0x40081708:0x3ffbc8d0 0x3ffbc98d:0x3ffbc9d0 0x400d85b6:0x3ffbc9f0 0x40082926:0x3ffbca10 0 29 | x400822fe:0x3ffbca30 0x400dd591:0x3ffbcaa0 0x400dc019:0x3ffbcac0 0x400dc467:0x3ffbcae0 0x400dc5f5:0x 30 | 3ffbcb50 0x400db105:0x3ffbcbd0 0x400db1b4:0x3ffbcc50 0x400da27e:0x3ffbccb0 0x400da7b1:0x3ffbccf0 0x4 31 | 01396b2:0x3ffbcd10 0x401398a6:0x3ffbcd60 0x401398f9:0x3ffbcd90 0x401017ec:0x3ffbcdb0 0x401019fa:0x3f 32 | fbcde0 0x400d9721:0x3ffbce10 0x400 33 | ``` 34 | I use platformio so the elf file for my application is in 35 | `.pio/build/{environment}/{project}.elf` 36 | so I launched the esp-backtrace as: 37 | `esp32-backtrace .pio/build/usb/esp32-secure-base.elf` 38 | and I pasted the crash printout into its stdin and the result is: 39 | ``` 40 | PC: 0x40081708 is at esp-idf-public/components/freertos/xtensa_vectors.S:1118. 41 | BT-0: 0x40081708 is at esp-idf-public/components/freertos/xtensa_vectors.S:1118. 42 | BT-2: 0x400d85b6 is in esp_ipc_call (esp-idf-public/components/esp32/ipc.c:123). 43 | BT-3: 0x40082926 is in spi_flash_disable_interrupts_caches_and_other_cpu (esp-idf-public/components/ 44 | spi_flash/cache_utils.c:120). 45 | BT-4: 0x400822fe is in spi_flash_read (esp-idf-public/components/spi_flash/flash_ops.c:154). 46 | BT-5: 0x400dd591 is in nvs::nvs_flash_read(unsigned int, void*, unsigned int) (esp-idf-public/compon 47 | ents/nvs_flash/src/nvs_ops.cpp:70). 48 | BT-6: 0x400dc019 is in nvs::Page::readEntry(unsigned int, nvs::Item&) const (esp-idf-public/componen 49 | ts/nvs_flash/src/nvs_page.cpp:717). 50 | BT-7: 0x400dc467 is in nvs::Page::findItem(unsigned char, nvs::ItemType, char const*, unsigned int&, 51 | nvs::Item&, unsigned char, nvs::VerOffset) (esp-idf-public/components/nvs_flash/src/nvs_page.cpp:76 52 | 1). 53 | BT-8: 0x400dc5f5 is in nvs::Page::readItem(unsigned char, nvs::ItemType, char const*, void*, unsigne 54 | d int, unsigned char, nvs::VerOffset) (esp-idf-public/components/nvs_flash/src/nvs_page.cpp:266). 55 | BT-9: 0x400db105 is in nvs::Storage::readMultiPageBlob(unsigned char, char const*, void*, unsigned i 56 | nt) (esp-idf-public/components/nvs_flash/src/nvs_storage.cpp:430). 57 | BT-10: 0x400db1b4 is in nvs::Storage::readItem(unsigned char, nvs::ItemType, char const*, void*, uns 58 | igned int) (esp-idf-public/components/nvs_flash/src/nvs_storage.cpp:455). 59 | BT-11: 0x400da27e is in nvs_get_str_or_blob(nvs_handle, nvs::ItemType, char const*, void*, size_t*) 60 | (esp-idf-public/components/nvs_flash/src/nvs_api.cpp:515). 61 | BT-12: 0x400da7b1 is in nvs_get_blob(nvs_handle, char const*, void*, size_t*) (esp-idf-public/compon 62 | ents/nvs_flash/src/nvs_api.cpp:525). 63 | BT-18: 0x400d9721 is in esp_wifi_init (esp-idf-public/components/esp32/wifi_init.c:51). 64 | BT-24: 0x4008b3ba is in prvProcessExpiredTimer (esp-idf-public/components/freertos/timers.c:524). 65 | BT-25: 0x4008b3ed is in prvProcessTimerOrBlockTask (esp-idf-public/components/freertos/timers.c:571) 66 | . 67 | BT-26: 0x4008b508 is in prvTimerTask (esp-idf-public/components/freertos/timers.c:544). 68 | BT-27: 0x40088781 is in vPortTaskWrapper (esp-idf-public/components/freertos/port.c:143). 69 | ``` 70 | This tells me that the problem happens in an Arduino system task somewhere in 71 | `esp_wifi_init` (see BT-18). 72 | 73 | ### Notes: 74 | - the backtrace shows the inner-most frame first (BT-0) and then goes back in history. 75 | - instead of pasting the exception text on stdin you can also specify a file to read from as second 76 | argument. 77 | - it is OK to have the "Backtrace:" line from the exception text be split up due to copy&paste 78 | line-wrapping caused by the terminal emulator, the script joins them back together. 79 | 80 | Issues 81 | ------ 82 | - While the script handles `Backtrace` continuation lines it doesn't join addresses that get split 83 | at the end of the terminal window. 84 | - GDB is invoked for each address, which is inefficient. It works ok for me, but it would be more 85 | elegant to construct a script for GDB and pipe that in so GDB only reads and parses the elf file 86 | once. 87 | - Anything that looks like an address in a register will be printed, whether it's live data or some 88 | stale value. 89 | --------------------------------------------------------------------------------