Найти тему
K12 :: О ESP32 и не только

Удаленный перехват backtrace в случае сбоя без физического подключения к устройству

Оглавление

Добрый день, уважаемый читатель! При локальной отладке в случае сбоя паники в COM-порт обычно печатается backtrace, который можно “расшифровать” и выяснить место точку возникновения проблемы. Зная файл исходного кода и номер строки, можно с большой степенью вероятности предположить причину проблемы и устранить её. В прошлой статье я рассказал, как можно расшифровать backtrace для поиска причины сбоя на устройстве.

Но иногда уже после сборки и отладки очередного устройства, а затем и установки его “на место”, вдруг начинают проявляться самопроизвольные перезагрузки из-за паники. Возникает довольно непростая задача: определить место возникновения сбоя без возможности просмотреть логи в терминале.

Но и при доступе к устройству так же могут быть некоторые сложности – ошибка может возникать раз в неделю или реже, в этом случае “мониторить” устройство через COM-порт становится проблематично. В PlatformIO имеется возможность сохранять отладочный вывод в файл, можно использовать этот способ, но это не всегда удобно.

Гораздо хуже, если физического доступа к устройству вообще нет – например, если оно физически расположено в другом населенном пункте или в труднодоступном месте. В таком случае отладка возможна, только если устройство самостоятельно отправит какие-либо данные для отладки в telegram, на mqtt-брокер или куда-либо ещё.

Проблема в том, что на текущий момент ни framework-espidf, ни framework-arduinoespressif32 не предусматривают штатной возможности отправки backtrace куда-либо, кроме COM-порта. Так что придется “колхозить” перехват и сохранение backtrace самостоятельно.

Массив для сохранения backtrace

Под сохранение backtrace достаточно выделить сравнительно небольшой массив uint32_t чисел. Размер массива зависит от того, сколько адресов нужно там хранить. Обычно достаточно 10-16 ячеек, но в некоторых особо тяжелых случаях нужно и больше (например если перезагрузка возникает при ошибке выделения памяти, когда куча исчерпана – в этом случае в начале backtrace будут вызовы системных функций выделения памяти). Я определил этот массив так:

-2

С помощью макроса CONFIG_RESTART_DEBUG_INFO можно включать или отключать сохранение отладочной информации, с помощью макроса CONFIG_RESTART_DEBUG_STACK_DEPTH можно устанавливать размер буфера под сохранение backtrace. Кроме того, в данной структуре сохраняются данные о размере свободной кучи.

Возникает вопрос в том, где хранить эти данные при перезагрузке.

Первая мысль – сохранить во flash памяти, например в NVS-разделе. Но, увы, это не всегда прокатывает, так как не удается выполнить запись (собственно из-за сбоя).

В итоге я поступил по другому – пометил данные для отладке модификатором __NOINIT_ATTR. Это заставит компилятор поместить данный блок данных в область, которая не очищается при перезагрузке. Конечно, при выключении и включении устройства данные будут утеряны в любом случае, но в этом случае они и не требуются.

-3

Заполнение данных для backtrace

Как я уже упомянул выше, в ESP-IDF не предусмотрен вывод backtrace куда-либо ещё, кроме com-порта. Что ж, придется сделать это самостоятельно. Для этого я воспользовался функцией esp_backtrace_print из файла \.platformio\packages\framework-espidf\components\xtensa\debug_helpers.c, и написал похожий вариант:

-4

А затем:

-5

При сохранении данных stk_frame.sp не сохраняется, так как это особо не влияет на расшифровку адресов. Осталось обеспечить вызов этой функции перед перезагрузкой и в случае паники.

Сохранение backtrace перед перезагрузкой устройства

Реализовать сохранение данных при “штатной” перезагрузке устройства командой esp_restart очень просто. Для этого достаточно зарегистрировать обработчик “штатной” перезагрузки. Всего можно использовать от 1 до 5 обработчиков, и это число, увы, изменить нельзя.

-6

Это обеспечит сохранение отладочной информации в зарезервированной области памяти при перезагрузке из-за обновления OTA или в других “нормальных” ситуациях.

Но, увы, при “панике” из-за ошибки этот метод не сработает. Решить эту задачу для ESP-IDF можно, создав wrapper для системной функции esp_panic_handler с помощью опции -Wl. Для этого создаем обертку для esp_panic_handler:

-7

Затем в файл CMakeLists.txt необходимо добавить строку idf_build_set_property(LINK_OPTIONS “-Wl,–wrap=esp_panic_handler” APPEND):

-8

Это заставит компилятор вызвать нашу функцию esp_panic_handler вместо “штатной”, а затем уже наша функция вызовет встроенную. Вуаля!

Отправка backtrace после перезагрузки устройства

Осталось только отправить эти данные после перезагрузки куда-нибудь на сервер. Например это можно сделать, опубликовав эти данные на mqtt-брокер. А можно отправить в telegram, добавив backtrace к информации о запуске устройства:

-9

Затем:

-10

Теперь при “программной перезагрузке” или сбое получим примерно такое сообщение:

-11

Можно скопировать эти данные в attr2line и расшифровать место возникновения ошибки. Задача решена.

Где всё это скачать?

Как всегда - на GitHub. Отдельного модуля / библиотеки для отладки не предусмотрено, функции интегрированы в библиотечку reEsp32:

GitHub - kotyara12/reEsp32: ESP32 system functions and utilites (ESP-IDF)

Чтобы было понятней, немного поясню код:

Предупреждение: при модификации библиотеки в будущем строки могут "съехать"

Понимаю, тема не очень простая - задавайте вопросы в комментариях.

_______________

На этом пока всё, до встречи на сайте и на dzen-канале!

👍 Понравилась статья? Поддержите канал лайком или комментарием! Каналы на Дзене "живут" только за счет ваших лайков.

📌Подпишитесь на канал и вы всегда будете в курсе новых статей.

🔶 Полный архив статей вы найдете здесь

Благодарю за вашу поддержку! 🙏