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

StackOverflow https://stackoverflow.com/questions/126036

Вопрос

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

Давайте возьмем какую-нибудь функцию :

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

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

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

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

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

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

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

Было бы неплохо сделать что-то похожее на то, что существует для 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 означает, что размер фрейма функции не является статическим.Это происходит в основном, когда некоторые локальные переменные имеют динамический размер.Когда этот классификатор появляется один, второе поле не является надежным показателем анализа стека функций.Когда оно указано с помощью bounded , это означает, что второе поле является надежным максимумом использования стека функций.

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

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

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

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

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

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

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

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

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

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

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

Чтобы реализовать это, вам необходимо:

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top