Перейти к содержанию

Как найти переполнение стека задачи 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, только если в программе нет собственного. Функция получает дескриптор и имя задачи; после переполнения состояние памяти ненадёжно, поэтому в обработчике лучше только сохранить имя задачи и перезагрузиться.

Источники

  1. 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 байта
  2. 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 байт от конца — сочетают с другим методом
  3. 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 равно байту

Нашли ошибку?