Расшифровка отладочной информации ESP32
Отладка программы на микроконтроллере — необходимый навык для любого инженера, занятого разработкой электронных устройств. Методов отладки множество: мигание светодиодами, вывод ошибок в UART, отладка через специальные интерфейсы JTAG/SWD. В этой статье мы обратим внимание на отладку программы, написанной для микроконтроллера ESP32 (по факту SoC).
Данный микроконтроллер имеет встроенный механизм формирования отладочной информации и вывода её через UART-интерфейс. Так выглядит кадр отладочной информации ESP32-S3, формируемый при возникновении ошибки. Многие, кто программирует для ESP32, встречали такой лог и не знали, что с ним делать.
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC: 0x4200cd53 PS: 0x00060b30 A0: 0x8200d1cb A1: 0x3fca3730
A2: 0x3fc9ea6c A3: 0x000000b4 A4: 0x000000be A5: 0x00000000
A6: 0x00000014 A7: 0x3fca3820 A8: 0x00000078 A9: 0x00000000
A10: 0x00000001 A11: 0x00000000 A12: 0x00000000 A13: 0x00000000
A14: 0x00000000 A15: 0x000000b4 SAR: 0x0000000a EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000000 LBEG: 0x4200bcb4 LEND: 0x4200bcbe LCOUNT: 0x00000000
Backtrace: 0x4200cd50:0x3fca3730 0x4200d1c8:0x3fca3880 0x42007078:0x3fca38b0 0x4200888c:0x3fca3900 0x4200560f:0x3fca3920 0x42005c47:0x3fca3940 0x42016e68:0x3fca3960 0x4037cff6:0x3fca3980
Если посмотреть на этот набор шестнадцатеричных чисел взглядом IT-обывателя, то он мало что прояснит. Сверху — непонятное сообщение об ошибке. Ниже (Core 1 register dump) — состояние всех регистров. Ещё ниже (Backtrace) — вероятно, трассировка стека, но только без названий функций.
Примечание
Трассировка стека — это отчёт о цепочке вызовов функций, которая привела к ошибке. Это очень ценная информация для поиска ошибки. Именно её расшифровкой мы и займёмся.
Необходимая информация для расшифровки хранится в файле .elf, который генерируется во время компиляции программы. А для анализа этих .elf-файлов в esp-idf существуют специальные утилиты — addr2line.
Итак, чтобы расшифровать трассировку стека в ESP32, понадобится:
утилита
addr2line(для ESP32-S3 этоxtensa-esp32s3-elf-addr2line);файл
.elf.
Где взять утилиту addr2line?
Эта утилита устанавливается вместе с набором инструментов esp-idf. Соответственно, самый простой способ её получить — установить esp-idf. Ссылка на инструкцию по установке есть в конце статьи.
Утилиты для работы с конкретными ядрами микроконтроллеров живут в папке ~/.espressif/tools. Зайдём в эту папку и выберем подходящий пакет. Для работы с ESP32, ESP32-S2 или ESP32-S3 откроем папку xtensa-esp-elf. А вот у ESP32-C6 ядро другое — RISC-V, так что и утилита будет совершенно другая, в папке riscv32-esp-elf.
Спускаемся вниз до папки bin и находим там xtensa-esp32s3-elf-addr2line — это то, что нам нужно!
Также наличие нужной утилиты можно проверить так:
which xtensa-esp32s3-elf-addr2line
Подсказка
Видно утилиту будет только после подключения окружения esp-idf командой source. Как это сделать, читай ниже.
Где взять .elf?
Этот файл формируется каждый раз при сборке проекта для целей отладки и анализа. В отличие от .hex-файла, в .elf хранятся имена функций, что нам как раз и необходимо для расшифровки отладочной информации.
Например, в среде Arduino IDE файл лежит во временной папке проекта:
C:\Users\master\AppData\Local\arduino\sketches\26D11705A98E98C54B438CA83C79660E\test.ino.elf
Расшифровка отладочной информации (backtrace)
Итак, по шагам.
Подключаем окружение esp-idf командой:
source export.sh
Запускаем утилиту расшифровки, передавая в неё
.elfи исходные коды отладочной информации:
xtensa-esp32s3-elf-addr2line -pfiaC -e test.ino.elf 0x4200cd50:0x3fca3730 0x4200d1c8:0x3fca3880 0x42007078:0x3fca38b0 0x4200888c:0x3fca3900 0x4200560f:0x3fca3920 0x42005c47:0x3fca3940 0x42016e68:0x3fca3960 0x4037cff6:0x3fca3980
Результат:
0x4200d1c8:0x3fca3880 0x42007078:0x3fca38b0 0x4200888c:0x3fca3900 0x4200560f:0x3fca3920 0x42005c47:0x3fca3940 0x42016e68:0x3fca3960 0x4037cff6:0x3fca3980
0x4200cd50: TFT_eSPI::pushImage(long, long, long, long, unsigned short*, unsigned short) at C:\Users\olege\AppData\Local\Programs\Arduino IDE/c:\Users\olege\Documents\Arduino\libraries\TFT_eSPI/TFT_eSPI.cpp:1504
0x4200d1c8: TFT_eSPI::frameEnd() at C:\Users\olege\AppData\Local\Programs\Arduino IDE/c:\Users\olege\Documents\Arduino\libraries\TFT_eSPI/TFT_eSPI.cpp:2469
0x42007078: SDGFX::drawItemBar(Item&, unsigned char) at C:\Users\olege\AppData\Local\Programs\Arduino IDE/C:\Devel\Projects\1. ROC\ROC.TFT-ESP-S3\src\RobotClass_Photon_S3/sdgfx.cpp:450
0x4200888c: SDGFX::drawItem(Item&, unsigned char) at C:\Users\olege\AppData\Local\Programs\Arduino IDE/C:\Devel\Projects\1. ROC\ROC.TFT-ESP-S3\src\RobotClass_Photon_S3/sdgfx.cpp:191
0x4200560f: handleGFX() at C:\Users\olege\AppData\Local\Programs\Arduino IDE/C:\Devel\Projects\1. ROC\ROC.TFT-ESP-S3\src\RobotClass_Photon_S3/RobotClass_Photon_S3.ino:440 (discriminator 2)
(inlined by) handleGFX() at C:\Users\olege\AppData\Local\Programs\Arduino IDE/C:\Devel\Projects\1. ROC\ROC.TFT-ESP-S3\src\RobotClass_Photon_S3/RobotClass_Photon_S3.ino:426 (discriminator 2)
0x42005c47: loop() at C:\Users\olege\AppData\Local\Programs\Arduino IDE/C:\Devel\Projects\1. ROC\ROC.TFT-ESP-S3\src\RobotClass_Photon_S3/RobotClass_Photon_S3.ino:619
0x42016e68: loopTask(void*) at C:\Users\olege\AppData\Local\Programs\Arduino IDE/C:\Users\olege\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.1.1\cores\esp32/main.cpp:74
0x4037cff6: vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139
Совет
Теперь понятно, куда копать. В данном коде ошибка была в функции drawItemBar. Она была успешно изолирована и устранена!
Полезные ссылки
Инструкция по установке esp-idf на Linux: