Профилирование Памяти На Stm32 И Других Микроконтроллерах: Статический Анализ Размера Стека

Привет, Хабр! В последняя статья Я сам об этом упомянул, и в комментариях спросили - ок, окей, научным методом мы подобрали размер стека, вроде ничего не падает, но можно ли как-то более достоверно прикинуть, чему он равен а кто вообще столько сожрал? Короткий ответ: да, но нет. Нет, с помощью методов статического анализа невозможно точно измерить размер стека, необходимый программе — но, тем не менее, эти методы могут быть полезны.

Ответ чуть длиннее — под катом.

Как известно большинству людей, пространство стека по существу выделяется для локальных переменных, которые используются выполняющейся в данный момент функцией — за исключением переменных с модификатором static, которые хранятся в статически выделенной памяти, в области bss, поскольку они должны сохранять свои значения между вызовами функций.

При выполнении функции компилятор добавляет в стек место для необходимых ему переменных, а по завершении освобождает это пространство обратно.

Казалось бы, все просто, но – и это очень жирно.

Но — у нас есть несколько проблем:

  1. функции вызывают внутри другие функции, которым также нужен стек
  2. иногда функции вызывают другие функции не путем их прямого упоминания, а по указателю на функцию
  3. В принципе возможно (хотя этого следует избегать любой ценой) рекурсивно вызывать функции, когда A вызывает B, B вызывает C, а C внутри себя снова вызывает A.
  4. в любой момент может произойти прерывание, обработчиком которого является та же функция, которая хочет свой кусок стека
  5. если у вас есть иерархия прерываний, внутри прерывания может произойти другое прерывание!
Из этого списка обязательно стоит вычеркнуть рекурсивные вызовы функций, ведь их наличие — повод не считать объем стека, а пойти высказать свое мнение автору кода.

Все остальное, увы, в общем случае удалить нельзя (хотя в частности могут быть нюансы: например, все ваши прерывания могут иметь одинаковый приоритет по задумке, например, как в ОС RIOT, и вложенных прерываний не будет ).

А теперь представьте себе картину маслом:

  • функция A, съевшая 100 байт в стеке, вызывает функцию B, которой нужно 50 байт
  • на момент выполнения B сам A явно еще не завершился, поэтому его 100 байт не освобождаются, поэтому в стеке у нас уже есть 150 байт
  • функция B вызывает функцию C, и делает это с помощью указателя, который, в зависимости от логики программы, может указывать на полдюжины различных функций, занимающих от 5 до 50 байт стека
  • Во время выполнения C происходит прерывание с тяжелым обработчиком, который работает относительно долго и потребляет 20 байт стека.

  • Во время обработки прерывания возникает другое прерывание с более высоким приоритетом, обработчик которого требует 10 байт стека.

В этом прекрасном исполнении, при особо удачном стечении всех обстоятельств, у вас будет не менее пяти одновременно активных функций - A, B, C и два обработчика прерываний.

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

Очевидно, что для любого автоматического статического анализатора кода эта задача крайне близка к невыполнимой и может быть решена лишь в грубом приближении к верхней границе:

  • суммировать стеки всех обработчиков прерываний
  • суммировать стеки функций, выполняемых в одной ветке кода
  • попытаться найти все указатели на функции и их вызовы и принять в качестве размера стека максимальный размер стека среди функций, на которые указывают эти указатели
В большинстве случаев результатом будет, с одной стороны, сильно завышенная оценка, а с другой — шанс пропустить какой-нибудь особо хитрый вызов функции через указатели.

Поэтому в целом мы можем просто сказать: эта проблема не решается автоматически .

Ручное решение, выполняемое человеком, знающим логику программы, требует обработки довольно большого количества чисел.

Тем не менее статическая оценка размера стека может быть очень полезна при оптимизации ПО — хотя бы для банальной цели понять, кто сколько ест, и не слишком ли это много.

Для этой цели в наборе инструментов GNU/gcc есть два чрезвычайно полезных инструмента:

  • -fstack-флаг использования
  • утилита cflow
Если к флагам gcc добавить -fstack-usage (например, в Makefile в строке с CFLAGS), то для каждый скомпилированный файл %filename%.

c, компилятор создаст файл %filename%.

su, внутри которого будет простой и понятный текст. Возьмем, к примеру, target.su для эта гигантская портянка :

   

target.c:159:13:save_settings

Теги: #Программирование микроконтроллеров #Производство и разработка электроники #микроконтроллеры #stm32 #Электроника для начинающих #cortex #cortex-m3
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.