Как найти переполнение стека задачи FreeRTOS на ESP32?
Обновлено Редакция GAW
Задаче FreeRTOS при создании выделяют стек фиксированного размера. Если вложенные вызовы, большие локальные массивы или форматированный вывод его превышают, задача портит чужую память — и падение происходит позже и в другом месте. Как устроены переполнения в общем случае, описано в статье «Переполнение стека».
Способы обнаружения в ESP-IDF
| Способ | Опция | Когда ловит | Ограничение |
|---|---|---|---|
| По указателю стека (метод 1) | CHECK_STACKOVERFLOW_PTRVAL | при переключении задач | пропускает переполнение между переключениями |
| «Канарейка» (метод 2) | CHECK_STACKOVERFLOW_CANARY, по умолчанию | при переключении задач | не гарантирует обнаружения |
| Точка наблюдения | WATCHPOINT_END_OF_STACK | сразу при записи в последние 32 байта | стек меньше до 60 байт, у gdb одной точкой меньше |
| Запас стека | uxTaskGetStackHighWaterMark | по запросу | показывает минимум за время наблюдения |
Канарейка — это шаблон, которым FreeRTOS заполняет конец стека при создании задачи; если байты изменились, вызывается vApplicationStackOverflowHook. Точка наблюдения — аппаратный механизм отладки («Точка наблюдения»), он останавливает программу в момент записи.
Сколько стека занято
// Free stack of the current task, ESP-IDF: StackType_t is a byte
UBaseType_t free_bytes = uxTaskGetStackHighWaterMark(NULL);
ESP_LOGI("stack", "min free: %u bytes", (unsigned)free_bytes);
В исходном FreeRTOS функция возвращает «слова» StackType_t, на 32-битном ядре по 4 байта. В ESP-IDF StackType_t — байт, и размер стека в xTaskCreate тоже задаётся в байтах, поэтому числа можно сравнивать напрямую. Этот же принцип заполнения и поиска нетронутой части — раскраска стека.
Если переполнение уже привело к падению, разберите лог паники — ответ «Что значит Guru Meditation Error: LoadProhibited». Модули ESP32 — на странице ESP32.
Похожие вопросы
Почему проверка не сработала, а программа упала с Guru Meditation?
Методы 1 и 2 проверяют стек только при переключении задач. Если переполнение испортило память и программа упала раньше, проверка не успела. По Kconfig ESP-IDF для этого есть точка наблюдения на последних 32 байтах стека — она срабатывает сразу при записи, но не ловит запись, которая перескочила дальше 32 байт.
Сколько стека оставлять в запасе?
Единого правила нет: смотрят на минимум uxTaskGetStackHighWaterMark после прогона всех сценариев, включая обработку ошибок и вывод логов, и оставляют запас. Значение показывает лучший случай за время наблюдения — редкая глубокая цепочка вызовов в него могла не попасть (это ограничение любой раскраски стека).
Можно ли переписать стандартный обработчик переполнения?
Да: по Kconfig ESP-IDF подставляет свой vApplicationStackOverflowHook, только если в программе нет собственного. Функция получает дескриптор и имя задачи; после переполнения состояние памяти ненадёжно, поэтому в обработчике лучше только сохранить имя задачи и перезагрузиться.
Источники
- FreeRTOS. FreeRTOS-Kernel V11.3.3, include/stack_macros.h и include/task.h — stack_macros.h: configCHECK_FOR_STACK_OVERFLOW = 1 — сравнение вершины стека с границей при выключении задачи; больше 1 — ещё проверка последних байтов, заполненных при создании задачи (шаблон 0xa5a5a5a5); второй способ не гарантирует обнаружения; при срабатывании вызывается vApplicationStackOverflowHook(задача, имя); task.h: uxTaskGetStackHighWaterMark — минимум свободного стека с запуска задачи в словах, на 32-битной машине 1 = 4 байта
- Espressif Systems. ESP-IDF v6.1, components/freertos/Kconfig — FREERTOS_CHECK_STACKOVERFLOW: NONE (0), PTRVAL — метод 1, по указателю стека при переключении, быстрый, но пропускает переполнения между переключениями (1), CANARY — метод 2, по «волшебным» байтам в конце стека (2), по умолчанию CANARY; если своего vApplicationStackOverflowHook нет, ESP-IDF подставляет свой; FREERTOS_WATCHPOINT_END_OF_STACK: последняя аппаратная точка наблюдения на последние 32 байта стека, срабатывает сразу при записи, у gdb становится на одну точку меньше, полезный стек уменьшается до 60 байт из-за выравнивания, ловит только запись в пределах 32 байт от конца — сочетают с другим методом
- Espressif Systems. ESP-IDF v6.1: freertos_idf.rst и portable/xtensa/include/freertos/portmacro.h — freertos_idf.rst: в IDF FreeRTOS размер стека задачи задаётся в байтах, а не в словах, как в исходном FreeRTOS; portmacro.h (Xtensa): portSTACK_TYPE — uint8_t, StackType_t — байт, поэтому «слово» стека в функциях FreeRTOS на ESP32 равно байту
Нашли ошибку?
Смотрите также
- Переполнение стека и памятьОценка стека, раскраска ОЗУ, защитная область MPU, HardFault, куча
- Раскраска стекаЗаполнение памяти шаблоном при старте для измерения максимальной глубины стека
- Что значит Guru Meditation Error (LoadProhibited) на ESP32?EXCVADDR = 0 — NULL-указатель; место ошибки — первая строка Backtrace
- Точка наблюденияАппаратный останов ядра при чтении или записи заданного адреса
- ESP32: выводы и GPIO34 GPIO: функции, свободные выводы, только входы, флеш-память, strapping, JTAG, токи