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

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

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

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

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

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

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

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

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

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


person shodanex    schedule 24.09.2008    source источник
comment
Если вам интересно, я ввел секретный символ '}'   -  person 1800 INFORMATION    schedule 24.09.2008
comment
Мне вопрос кажется непонятным. Думаю, если вы напишете больше о том, почему вы хотите это знать и почему проверка дизассемблера или исполняемого файла (что является самым простым способом проверить вывод компилятора) неприемлема, возможно, кто-то сможет найти какое-то простое решение?   -  person Suma    schedule 24.09.2008


Ответы (7)


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

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

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

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

person Community    schedule 12.01.2009
comment
Обновление: Майкл Грин отметил ниже, что GCC 4.6 имеет -fstack-usage, доступный для C: gcc.gnu.org/gcc-4.6/changes.html; -fstack-usage описано ниже в ответе shodanex ниже: stackoverflow.com/a/126490/53974. - person Blaisorblade; 02.09.2012

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

Что-то похожее на то, что существует для ADA, было бы хорошо. Взгляните на эту справочную страницу из руководства gnat:

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

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

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

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

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

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

person shodanex    schedule 24.09.2008
comment
Это старый вопрос, но fwiw GCC 4.6 имеет -fstack-usage, доступный для C: gcc .gnu.org / gcc-4.6 / changes.html - person Michael Greene; 26.04.2011
comment
@ Майкл Грин: это может быть ответ! Спасибо ! - person shodanex; 27.04.2011

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

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

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

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

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

person Isak Savo    schedule 24.09.2008
comment
StackAnalyzer кажется приятным, но он не выполняет запрошенную работу, поскольку анализирует исполняемый файл, а не исходный код. Хотя написание такого инструмента действительно должно быть теоретически возможным, я не думаю, что существует такой инструмент - проверка времени выполнения использования стека или на основе сборки очень практична. - person Suma; 24.09.2008
comment
Я просто называю одно имя функции, и вы знаете, почему статического анализа кода недостаточно для получения используемого пространства стека функции: alloca. Как только одна функция использует его (со значением, отличным от const), вы не можете этого сделать. Еще одна вещь, о которой уже упоминалось: рекурсия. - person flolo; 12.01.2009

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

person 1800 INFORMATION    schedule 24.09.2008
comment
теоретически инструменты статического анализатора кода, такие как lint, могут справиться с этой задачей, но я практически не думаю, что они способны. - person Ilya; 24.09.2008

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

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

person Will Dean    schedule 24.09.2008

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

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

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

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

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

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

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

person Suma    schedule 24.09.2008

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

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

person xmjx    schedule 24.09.2008
comment
Вы говорите о стеке процессов, а не о том, сколько каждый вызов функции будет использовать - person shodanex; 24.09.2008
comment
Проблема остановки не останавливает статический анализ (то есть в компиляторах) и дает приблизительные ответы, глядя на текст программы. Фактически, большая проблема - это вычисление, когда две разные программы эквивалентны, поэтому две эквивалентные программы могут давать разные результаты при статическом анализе. - person Blaisorblade; 12.01.2009