Как узнать максимальное используемое пространство стека?

Я создаю скетч Arduino, который включает в себя довольно много классов, и я хочу проверить, будет ли он работать на Arduino Uno или Mega (2 КБ против 8 КБ SRAM).

На данный момент мои глобальные переменные занимают около 1200 байт, и я ожидаю, что эта цифра увеличится. Однако помимо этого мне нужно убедиться, что стековая память не используется слишком много. Как найти максимальный используемый размер стека?.

Я могу проверить вручную, рассчитав максимальный стек функций и вычислив все параметры + локальные переменные, но это очень утомительная работа (тем более, что мое приложение все еще сильно меняется).

Или другой способ: я знаю, что можно получить свободное место, но должен ли я добавить его в «стратегические» места или в начало всех функций и продолжить оттуда? (их будет много).

Или есть какой-то статический способ проверить это?

Приложение работает в Visual Studio C++ как отдельное приложение, так что, возможно, это поможет найти какое-то решение.

Я ожидаю, что размер стека легко составит несколько сотен байт (что довольно много для Arduino Uno SRAM).

, 👍2

Обсуждение

Всегда можно использовать Atmega1284P с пространством SRAM 16 КБ. У меня был проект с 45 сдвиговыми регистрами, 325 строками данных, которые обновлялись и передавались через SPI с частотой 20 кГц, и использовал 14 625 байт SRAM., @CrossRoads

@CrossRoads: Могу ли я найти (дешевую) плату, совместимую с Arduino, с AtMega1284P? . На самом деле, в качестве альтернативы я могу использовать Mega, а для моего «основного» проекта, где мне нужно больше памяти, я собираюсь использовать STM32 позже., @Michel Keijzers

Посмотрите здесь, www.crossroadsfencing.com/BobuinoRev17. Добавьте 1284 основных файла в среду разработки Arduino https://github.com/MCUdude/MCUdude_corefiles., @CrossRoads

Следует уточнить: речь идет о куче (как в заголовке) или о стеке (как в теле вопроса)?, @Edgar Bonet

@CrossRoads здорово иметь возможность использовать это, но я увидел, что помимо не такой уж дешевой цены, я предпочитаю использовать Mega и другой процессор, чтобы выполнять настоящую работу. Но приятно знать, что есть процессоры AtMega, способные на большее (я этого не знал)., @Michel Keijzers

@EdgarBonet Да, ошибка с моей стороны, я исправил, спасибо., @Michel Keijzers

Возможно, вы захотите использовать рисование стека или спросить GCC об использовании стека, добавив -fstack-usage, оба варианта см. https://stackoverflow.com/a/6390246/5296568. Существуют также инструменты пост-анализа, такие как https://github.com/PeterMcKinnis/WorstCaseStack. Также см. https://ucexperiment.wordpress.com/2015/01/02/arduino-stack-painting/., @Maximilian Gerhardt

@MaximilianGerhardt Спасибо за эту информацию; возможно, я смогу использовать его в версии для Windows, чтобы заранее получить хорошее представление., @Michel Keijzers


2 ответа


Лучший ответ:

7

Узнать размер стека довольно просто: указатель стека изначально устанавливается в RAMEND и уменьшается по мере роста стека. Таким образом:

static inline size_t stack_size()
{
    return RAMEND - SP;
}

должен ли я добавить это в «стратегических» местоположениях [...]

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

Изменить: нет простого способа узнать максимальный размер стека. Ты можно было бы получить приближение путем выборки стека через равные промежутки времени, например:

#include <util/atomic.h>

volatile size_t min_sp = RAMEND;

ISR(TIMER0_COMPA_vect)
{
    if (SP < min_sp)
        min_sp = SP;
}

static inline size_t max_stack_size()
{
    size_t min_sp_copy;
    ATOMIC_BLOCK(ATOMIC_FORCEON) {
        min_sp_copy = min_sp;
    }
    return RAMEND - min_sp_copy;
}

Если вы включите прерывание TIMER0_COMPA в setup():

TIMSK0 |= _BV(OCIE0A);

тогда размер стека будет измеряться каждые 1,024 мс. Обратите внимание, однако, что этот ISR сам использует некоторое пространство стека для своих целей (9 байт в моем эксперименте), и что это не выборка других ISR или критические секции.

,

Спасибо, способ прерывания действительно хорош для проверки максимального размера. И полезно знать о переменных (регистрах) RAMEND/SP., @Michel Keijzers

@MichelKeijzers: RAMEND на самом деле является константой, определенной в <avr/io.h>. SP действительно является регистром ввода-вывода или, скорее, парой регистров., @Edgar Bonet

Благодарю за разъяснение., @Michel Keijzers

Предполагается, что вы не используете кучу: ни новых объектов, ни объектов Malloc, ни объектов String. В любом случае, хорошая идея для небольших контроллеров., @DataFiddler

@DataFiddler: заголовок вопроса касался кучи (с тех пор был исправлен), но тело вопроса касалось стека. Мой ответ конкретно касается стека, а не кучи., @Edgar Bonet

Я не в восторге от твоего ответа, @Edgar. SP очень прост в использовании. Гораздо удобнее, чем (int)&aTempLocalVariable. Мой комментарий был всего лишь двумя центами на тему «размер стека не имеет значения, если вы не знаете свои требования к динамической куче»., @DataFiddler


7

Инициализировать все пространство SRAM от вершины кучи до нижней части стека либо во время установки, либо в модифицированной версии функции main(), предоставленной Arduino, с каким-то маловероятным шаблоном, например {0x55, 0xAA, ...} или "UnusedUnused...".

После того, как ваша программа поработает некоторое время, проверьте память на наличие грязных следов, читая с вершины кучи, чтобы найти наивысшее отклонение кучи, и вниз от указателя стека, чтобы найти самый низкий адрес, используемый стеком. Вы можете использовать отладчик, если он у вас есть, или просто hex & Дамп ASCII на терминал. Выберите его с помощью любых инструментов, которые у вас есть под рукой на вашем ПК. Если коллизий не было и вы используете дамп памяти, довольно легко начать с середины, которая не была нарушена, и прокрутить в любом направлении. Место, где ваш инициализированный шаблон был перезаписан, обычно очень четко выделяется.

,

Я проголосовал за это, потому что это другая идея, но я думаю, что просто распечатать значение кажется проще… или можно ли загрузить «шестнадцатеричный дамп» из работающей программы? (например, с помощью Arduino IDE), @Michel Keijzers

Я отправляю шестнадцатеричный дамп на терминал Arduino, сохраняю его в файл ПК и проверяю на ПК., @JRobert

Спасибо, тоже звучит как интересная идея. Спасибо, @Michel Keijzers