
Комментарии 29
Лучше не нули, а паттерн. Потому как какая-то функция сделает внутри себя
char myarray[100500] = {0};
strcpy(myarray, "hi");т.е. по факту стек был съеден, но не использован. А был бы каким-то 0xDEADBEEF заполнен - было бы видно...
Вместо "movs r3, #0" напишите "mov r3, #0xbeef", потом "movt r3, #0xdead".
Отличная идея! Только ведь стек не зануляется, когда мы выходим из подпрограммы ? Получается, что вы фиксируете максимальный уровень стека за все время наблюдений. Это отлично как аварийный критерий (например, можно переставать сбрасывать watchdog и вызывать перезагрузку контроллера - вместо продолжения работы с переполненным стеком и испорченными переменными). А вот если вы хотите соотнести использование стека с какими-то активностями - то вам надо понимать когда уровень воды в колодце опустился...
Лучше не нули, а паттерн. Потому как какая-то функция сделает внутри себя
char myarray[100500] = {0};
strcpy(myarray, "hi");т.е. по факту стек был съеден, но не использован. А был бы каким-то 0xDEADBEEF заполнен - было бы видно...
Лучше не нули, а паттерн.
Да. Это хорошее замечание. Мне пока не сосем понятно, как на assembler прописать паттерн.
Достаточно теперь прогнать модульные тесты, посмотреть на заполнения стека
можете пояснить этот момент? Обычно подобные тесты "крутятся" во framework-е, отличном от рабочей программы, и в тестах не будет повторения цепочки вызовов как в рабочем коде.
Для отладки потенциальных stackoverflow можно еще breakpoint на ячейку памяти поставить. На M0, если я правильно помню это можно только с помощью jtag-а
В программировании микроконтроллеров модульные тесты можно исполнять прямо внутри отладочной версии прошивки.
Прошивка тестирует сама себя изнутри.
и в тестах не будет повторения цепочки вызовов как в рабочем коде.
Глубина заполнения стека зачастую зависит от того какие входные данные поступают в прошивку по мере ее работы.
можете пояснить этот момент?
Модульное Тестирование в Embedded
https://habr.com/ru/articles/698092/
Во FreeRTOS эта раскраска уже реализована (сама проверка в функции prvTaskCheckFreeStackSpace, а получать результат можно через uxTaskGetSystemState). При этом если пользоваться FreeRTOS (или любой другой RTOS), то важен как раз стек в задачах, а стек для main который в задается LD файле не особо важен.
Э... глупый вопрос, а кто мешает просто посмотреть на значение регистра Stack Pointer, чтобы узнать, сколько места осталось в стеке?
Если вы это делаете в определенном месте (например, в главном цикле в main), то можете получать оттуда успокаивающие данные: стек почти не используется. А между этими замерами процедуры обработки прерываний, например, его переполняют. Самое неприятное - что никакой ошибки нет! Процедура прерывания загнала стек вверх, попортила чьи-то значения своими, благополучно отработала - и вернулась. А ошибка возникает позже, и в совершенно другом месте, не связанном логически с тем где реально переполнилось. А поскольку прерывания зависят от внешних источников - то оно может месяц нормально работать, а потом пошел звон по линии подключенного датчика - и вот процессор получает кучу прерываний по изменению уровня сигнала на ноге. А отваливается при этом индикация, или еще хуже - заменяются на случайные значения константы в pid-контроллере... А покраска стека хотя бы позволяет понять откуда ноги растут...
оно может месяц нормально работать, а потом пошел звон по линии подключенного датчика - и вот процессор получает кучу прерываний по изменению уровня сигнала на ноге
В этой ситуации и раскраска стека будет месяц показывать, что все нормально. Но да, согласен, она позволит надежно измерить пиковое заполнение стека. Хотя если в микроконтроллере есть штатные средства слежения за стеком (прерывание по определенному значению Stack Pointer), то лучше все-таки использовать их.
Хотя если в микроконтроллере есть штатные средства слежения за стеком (прерывание по определенному значению Stack Pointer), то лучше все-таки использовать их.
Это не штатные средства, а MPU, который еще надо сконфигурировать под стек.
MPU вообще всё равно есть стек или нет.
Ну, раз уж вы сослались на мою статью - отмечусь :)
В целом, способ хороший, но тоже не всеобъемлющий (наверное, ни один из возможных сам по себе таковым не является); так как по сути проверка происходит периодически - то переполнение можно и пропустить, если оно вклинивается в этот период, в смысле, если это уже на финальном изделии выполняется. В этом плане MPU или "мой" хак с HardFault'ом полезнее, т.к. позволит само переполнение перехватить как только мы за границы стека вылезаем. И на этапе отладки это тоже может быть удобнее, когда отладчик просто останавливается ровно на той строке, которая слишком много стека отъедает.
С другой стороны, ваш подход позволяет и статистику собрать и максимум за какое-то время оценить (хоть и не совсем бесплатно). Так что эти подходы друг другу не противоречат, а скорее дополняют друг друга.
Еще из поста не совсем понятно, как вы оценивали сложность разных подходов; на мой взгляд они все достаточно простые - особенно если не делать их с нуля, а брать готовые шаблоны.
Тут просили передать вам ссылку на разбор этого кода.
(Мопед не мой)
Вот видите? Господин mbr сам подтвердил мой культовый текст Hard Fault, что программисты микроконтроллеров в России говорят на одной сплошной звериной матерщине.
Это их интеллектуальный уровень.
Если отбросить скверную матерщину, то там получается хорошая инспекция программы. Можно слегка ускорить код и сделать его чуть более переносимым. Замечательно.
Это даже не привычно внезапно встретить настолько глубокий разбор.
Обычно на работе в РФ, если отдаешь код на review, то коллеги почему-то замечания дают только про оформление текстовых комментариев и про отступы
https://habr.com/ru/articles/837396/
, а сам Cи-код и алгоритм вообще не смотрят.
Даже не замечают.


Покраска Cтека (Stack Painting)