Подтвердить что ты не робот

Как определить максимальное использование стека во встроенной системе с помощью gcc?

Я пишу код запуска для встроенной системы - код, который загружает указатель начального стека, перед тем как перейти к функции main(), и мне нужно сказать, сколько байтов в стеке будет использоваться моим приложением (или несколько больше, скромный подсчет).

Мне сказали, что в gcc-компиляторе теперь есть опция -fstack-usage и опция -fcallgraph-info, которая может каким-то образом использоваться для статического вычисления точного "максимального использования стека" для меня. ("Анализ требований к суммарному времени стека с GCC" от Botcazou, Comar и Hainque).

Найджел Джонс говорит, что рекурсия - очень плохая идея во встроенных системах ("Вычисление размера стека" 2009), поэтому я старался не делать никаких взаимно рекурсивных функций в этом коде.

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

Без рекурсивных или повторных обработчиков прерываний он должен статически определять максимальное использование стека. (И поэтому большинство ответов на вопрос " Как определить максимальное использование стека" не применяются). Мое понимание - это я (или, желательно, некоторый бит кода на моем ПК, который автоматически запускается каждый раз, когда я перестраиваю исполняемый файл), сначала набирает максимальную глубину стека для каждого обработчика прерываний, когда он не прерывается прерыванием с более высоким приоритетом, а максимальное глубина стека функции main(), если она не прерывается. Затем я добавляю их все, чтобы найти общую (наихудшую) максимальную глубину стека. Это происходит (в моей встроенной системе), когда основная() фоновая задача находится на максимальной глубине, когда она прерывается прерыванием с наименьшим приоритетом, и это прерывание находится на максимальной глубине, когда оно прерывается следующим самым низким приоритетом прерывание и т.д.

Я использую YAGARTO с gcc 4.6.0 для компиляции кода для LM3S1968 ARM Cortex-M3.

Итак, как использовать параметр -fstack-usage и -fcallgraph-info с gcc для вычисления максимальной глубины стека? Или существует какой-то лучший подход для определения максимального использования стека?

(См. Как определить максимальное использование стека во встроенной системе? Почти для того же вопроса, нацеленного на компилятор Keil.)

4b9b3361

Ответ 1

Документы GCC:

-fstack-использование

Использует информацию об использовании стека вывода компилятора для программы на основе каждой функции. Имя файла для дампа создается путем добавления .su к auxname. auxname генерируется из имени выходного файла, если явно указано и не является исполняемым, в противном случае это базовое имя исходного файла. Запись состоит из трех полей:

  • Имя функции.
  • Число байтов.
  • Один или несколько классификаторов: статические, динамические, ограниченные.

Статический критерий означает, что функция статически статирует стек: фиксированное количество байтов выделяется для кадра при записи функции и освобождается при выходе функции; в функции не выполняются никакие корректировки стека. Второе поле - это фиксированное количество байтов.

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

Я не могу найти ссылки на -fcallgraph-info

Вы могли бы создать необходимую информацию из -fstack-использования и -fdump-tree-optimized

Для каждого листа в -fdump-tree-optimized, получите его родителей и суммируйте их размер размера стека (имея в виду, что это число лежит для любой функции с "динамическим", но не "ограниченным" ) из -fstack-usage, найдите максимальное значение этих значений, и это должно быть максимальное использование стека.

Ответ 2

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

GCC 4.6 добавляет параметр -fstack-usage, который дает статистику использования стека по принципу "по функциям".

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

Итак, похоже, что это можно сделать с помощью GCC, но вам может понадобиться сколотить правильный набор инструментов.

Ответ 3

Довольно поздно, но для тех, кто смотрит на это, ответы, связанные с объединением выходов из fstack-use и инструментов графа вызовов, таких как cflow, могут оказаться безумно неправильными для любого динамического распределения, даже ограниченного, потому что нет информации о том, когда что происходит динамическое распределение стека. Поэтому невозможно узнать, с какими функциями вы должны применить значение. В качестве надуманного примера, если (упрощенный) выход fstack-использования:

main        1024     dynamic,bounded
functionA    512     static
functionB     16     static

и очень простое дерево вызовов:

main
    functionA
    functionB

Наивный подход к их объединению может привести к тому, что main → functionA выбирается как путь максимального использования стека, равный 1536 байт. Но если наибольшее распределение динамических стеков в main() заключается в том, чтобы выталкивать большой аргумент как запись в функцию B() непосредственно в стек в условном блоке, который вызывает функцию B (я уже сказал, что это было надуманно), тогда действительно главная → functionB - путь максимального использования стека, равный 1040 байт. В зависимости от существующего дизайна программного обеспечения, а также для других более ограниченных целей, которые передают все в стек, кумулятивные ошибки могут быстро привести вас к поиску совершенно неправильных путей, требующих значительно завышенных максимальных размеров стека.

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

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

Ответ 4

Я не знаком с параметрами -fstack-usage и -fcallgraph-info. Тем не менее, всегда можно определить фактическое использование стека:

  • Выделить достаточное пространство стека (для этого эксперимента) и инициализировать его чем-то легко идентифицируемым. Мне нравится 0xee.
  • Запустите приложение и проверьте все его внутренние пути (по всем комбинациям ввода и параметров). Пусть он работает больше, чем "достаточно долго".
  • Изучите область стека и посмотрите, сколько было использовано в стеке.
  • Сделайте размер стека плюс 10% или 20%, чтобы переносить обновления программного обеспечения и редкие условия.

Ответ 5

В итоге я написал python script для реализации τεκ answer. Это слишком много кода для публикации здесь, но его можно найти на github