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

Проверка использования стека во время компиляции

Есть ли способ узнать и вывести размер стека, необходимый функции во время компиляции в C? Вот что я хотел бы знать:

Возьмем некоторую функцию:

void foo(int a) {
    char c[5];
    char * s;
    //do something
    return;
}

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

Я ищу что-то, что бы напечатать что-то вроде этого:

file foo.c: использование функции foo для стека n bytes

Есть ли способ не смотреть на сгенерированную сборку, чтобы это знать? Или ограничение, которое может быть установлено для компилятора?

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

Иначе говоря, можно ли узнать размер всех объектов, локальных для функции? Я думаю, что оптимизация компилятора не будет моим другом, потому что какая-то переменная исчезнет, ​​но верхний предел будет прекрасен.

4b9b3361

Ответ 1

Код ядра Linux работает на стеке 4K на x86. Поэтому им все равно. То, что они используют, чтобы проверить это, является perl script, который они написали, который вы можете найти как скрипты /checkstack.pl в недавнем архиве tar (у него есть 2.6.25). Он запускается на выходе objdump, документация по использованию находится в исходном комментарии.

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

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

Btw, с objdump из проекта mingw и ActivePerl, или с Cygwin, вы должны иметь возможность делать это также в Windows, а также в двоичных файлах, полученных с другими компиляторами.

Ответ 2

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

Нечто похожее на то, что существует для ADA, будет в порядке. Посмотрите на эту страницу руководства из руководства gnat:

22.2 Анализ использования статических стеков

Единица, скомпилированная с использованием -fstack-usage, будет генерировать дополнительный файл, который определяет максимальный размер используемого стека, для каждой функции. Файл имеет такое же базовое имя, что и файл целевого объекта с расширением .su. Каждая строка этого файла состоит из трех полей:

* The name of the function.
* A number of bytes.
* One or more qualifiers: static, dynamic, bounded. 

Второе поле соответствует размеру известной части функционального кадра.

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

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

Ответ 3

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

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

Конечно, ответ не может быть гарантирован на 100% точным, поскольку компилятор может выполнять различные виды оптимизации, такие как заполнение, помещение переменных в регистры или полное удаление ненужных переменных. Но любой ответ, который он дает, должен быть, по крайней мере, хорошей оценкой.

Я быстро просмотрел Google и нашел StackAnalyzer, но я предполагаю, что другие инструменты анализа статического кода имеют схожие возможности.

Если вам нужна 100% точная цифра, вам нужно будет посмотреть результат компилятора или проверить его во время выполнения (например, Ральф предложил его ответ)

Ответ 4

Только компилятор действительно знал бы, так как это парень, который объединяет все ваши вещи. Вам нужно будет взглянуть на сгенерированную сборку и посмотреть, сколько места зарезервировано в преамбуле, но на самом деле не учитываются такие вещи, как alloca, которые выполняют свою задачу во время выполнения.

Ответ 5

Предполагая, что вы находитесь на встроенной платформе, вы можете обнаружить, что ваш инструментарий имеет смысл. Хорошие коммерческие встроенные компиляторы (например, компилятор Arm/Keil) часто создают отчеты об использовании стека.

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

Ответ 6

Не совсем "время компиляции", но я бы сделал это как шаг после сборки:

  • пусть компоновщик создаст для вас файл карты.
  • для каждой функции в файле карты читается соответствующая часть исполняемого файла и анализируется пролог функции.

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

Для реализации этого вам необходимо:

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

Насколько легко или сложно это будет зависеть от вашей целевой платформы. (Встроенная? Какая архитектура процессора? Какой компилятор?)

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

Ответ 7

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

Скажем, у вас есть рекурсивная функция, уровень рекурсии которой зависит от ввода, который может быть произвольной длины, и вам уже не повезло.