Добрый день, уважаемый читатель! В данной статье будет предложен сравнительно простой способ дешифровки адресов backtrace в среде VSCode + PlatformIO без необходимости установки сторонних программ.
Под дешифровкой backtrace в контексте данной статьи понимается преобразование адресов в имена файлов и номера строк исходного кода. В настоящее время я занимаюсь разработкой для ESP32, поэтому в статье рассмотрен пример именно для этой конфигурации. Но, насколько я понимаю, этот способ вполне применим и для других микроконтроллеров, с которыми можно работать в PlatformIO.
При разработке устройств на микроконтроллерах иногда приходится сталкиваться с различными сбоями, вызванными допущенными ошибками при программировании. При возникновении критических ошибок микроконтроллер печатает дамп регистров и backtrace, а затем перезагружается. В разных ситуациях текст сообщения может быть различным, например, это может выглядеть так:
Для меня содержимое регистров пока не представляет особой ценности, а вот расшифровка backtrace (то есть преобразование адресов вызовов в имена файлов и номера строк) позволяет точно установить место возникновения исключительной ситуации в коде программы. Проанализировав место возникновения ошибки в большинстве случаев сразу же удается понять причину её появления.
Вместо предисловия
Во всех случаях для декодирования backtrace нам потребуется ELF-файл, в котором, собственно, и содержится вся необходимая отладочная информация для сопоставления адресов номерам строк исходного кода. Для PlatformIO и esp32dev этот файл можно найти в каталоге каталог проекта\.pio\build\esp32dev\firmware.elf.
Для начала стоить упомянуть об штатных (уже имеющихся, готовых) средствах для декодирования исключений.
В Arduino IDE для анализа исключений существует достаточно удобный плагин EspExceptionDecoder. Он позволяет легко и просто декодировать backtrace, но, увы, в PlatformIO не работает.
В PlatformIO имеется возможность включить дешифрацию адресов в номера строк в встроенном терминале “на лету”, прямо при выводе сообщений в терминал. Для этого в файле параметров проекта platformio.ini необходимо добавить следующие строки:
monitor_filters = esp32_exception_decoder
Про этот способ я рассказывал в предыдущей статье. Удобно? Казалось бы очень… На деле это приводит к очень сильному замедлению вывода сообщений в терминал, ведь попытка декодирования производится для каждой новой строки в терминале. Пришлось отказаться от этой сомнительной возможности.
Если взять "лопату" побольше и копнуть глубже, то можно увидеть, что на самом деле и EspExceptionDecoder и esp32_exception_decoder сами по себе ничего не декодируют, а просто используют для декодирования адресов внешнюю программу: addr2line. Эта программа поставляется вместе с компилятором и может различаться для разных платформ и микроконтроллеров. На эту же информацию указывает и справка для IDF Monitor:
Для ESP32 и ESP-IDF (toolchain-xtensa-esp32) ее можно найти здесь:
${env:USERPROFILE}\.platformio\packages\toolchain-xtensa-esp32\bin\xtensa-esp32-elf-addr2line.exe
Для других toolchain расположение и префикс утилиты могут различаться, но легко находятся поиском.
Параметры командной стройки для утилиты addr2line
В утилиту можно передавать не один адрес, а сразу несколько, разделенных пробелами. То есть можно передать сразу несколько адресов, и утилита расшифрует каждый из адресов отдельно.
Практическое применение
Для начала необходимо запустить командную строку Windows. Быстрее всего это делать, нажав клавиши “Win” + “R” на клавиатуре, а затем набрать команду “cmd“. Или через меню “Пуск” – “Служебные” – “Командная строка”. Затем в открытом окне вводим примерно следующую команду (правильные пути к файлам подставьте сами):
c:\Users\<ваш_профиль>\.platformio\packages\toolchain-xtensa-esp32\bin\xtensa-esp32-elf-addr2line.exe -pfiaC
-e c:\PlatformIO\dzen\.pio\build\esp32dev\firmware.elf
<backtrace>
где:
- c:\Users\<ваш_профиль> – путь к текущему профилю пользователя, подставьте свои данные
- toolchain-xtensa-esp32 – toolchain, который Вы используете, подставьте свои данные, если это необходимо
- xtensa-esp32-elf- – префикс, который зависит от toolchain, подставьте свои данные, если это необходимо
- c:\PlatformIO\dzen – путь к проекту, подставьте свои данные
- esp32dev – MCU или плата, под которую собирается проект, подставьте свои данные
- <backtrace> – вместо <backtrace> подставляем собственно перечисление адресов, без слова “Backtrace:”
… и нажимаем Enter. Например:
c:\Users\kotyara12\.platformio\packages\toolchain-xtensa-esp32\bin\xtensa-esp32-elf-addr2line.exe -pfiaC -e c:\PlatformIO\village_security\.pio\build\esp32dev\firmware.elf 0x4008150a 0x400d5e4f 0x40082121 0x400d5cd3 0x400e7da6
В результате, если все сделано правильно, получим примерно такие результаты:
На примере показан backtrace при получении “внешней” команды restart через mqtt. Команда оповещает системный цикл событий о перезапуске и запускает таймер на некоторое время, чтобы остальные службы смогли завершить свою работу. Что здесь видно: в последней строке таймер отработал положенное время и запустил callback-функцию espRestartTimer(void*), которая, в свою очередь, вызвала команду esp_restart. Во время завершения работы был вызван espDefaultShutdownHandler(), который, в свою очередь, вызвал debugUpdate(), где, собственно и произошло сохранение данного backtrace в области памяти NO_INIT, чтобы быть отправленным пользователю после перезагрузки.
В заключение небольшой совет: можно сохранить команду в cmd / bat файл и пользоваться более короткой записью.
На этом пока всё. Благодарю за внимание.
В следующей статье я предложу способ, как можно получить содержимое backtrace удаленно, без необходимости непосредственного подключения к контроллеру.
_______________
На этом пока всё, до встречи на сайте и на dzen-канале!
👍 Понравилась статья? Поддержите канал лайком или комментарием! Каналы на Дзене "живут" только за счет ваших лайков.
📌Подпишитесь на канал и вы всегда будете в курсе новых статей.
🔶 Полный архив статей вы найдете здесь
Благодарю за вашу поддержку! 🙏