31

C でコンパイル時に関数が必要とするスタックサイズを知り、出力する方法はありますか? これが私が知りたいことです:

いくつかの関数を取りましょう:

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

この関数をコンパイルするときに、関数が呼び出されたときにどれだけのスタック スペースが消費されるかを知りたいです。これは、大きなバッファを隠している構造体のオン スタック宣言を検出するのに役立つ場合があります。

私はこのようなものを印刷するものを探しています:

ファイル foo.c : 関数 foo スタックの使用量はnバイトです

それを知るために生成されたアセンブリを見ない方法はありますか? または、コンパイラに設定できる制限はありますか?

更新: 特定のプロセスのランタイム スタック オーバーフローを回避しようとしているわけではありません。コンパイラによって決定された関数スタックの使用がコンパイル プロセスの出力として利用可能かどうかを実行前に見つける方法を探しています。

別の言い方をすれば、関数にローカルなすべてのオブジェクトのサイズを知ることは可能ですか? 一部の変数は消えますが、上限は問題ないため、コンパイラの最適化は私の友人ではないと思います。

4

7 に答える 7

11

Linuxカーネルコードは、x86の4Kスタックで実行されます。したがって、彼らは気にします。彼らがそれをチェックするために使用するのは、彼らが書いたperlスクリプトであり、最近のカーネルtarball(2.6.25がそれを持っています)でscripts/checkstack.plとして見つけることができます。objdumpの出力で実行され、使用法のドキュメントは最初のコメントにあります。

私はすでに何年も前にユーザースペースバイナリにそれを使用したと思います、そしてあなたが少しのperlプログラミングを知っているなら、それが壊れていればそれを修正するのは簡単です。

とにかく、それが基本的に行うことは、GCCの出力を自動的に見ることです。そして、カーネルハッカーがそのようなツールを書いたという事実は、GCCでそれを行う静的な方法がないことを意味します(または、ごく最近追加されたのかもしれませんが、私はそうは思いません)。

ところで、mingwプロジェクトとActivePerlからのobjdumpを使用するか、Cygwinを使用すると、Windowsや他のコンパイラで取得したバイナリでもこれを実行できるはずです。

于 2009-01-12T08:59:27.627 に答える
8

StackAnlyserは、実行可能コード自体といくつかのデバッグ情報を調べているようです。この返信で説明されているのは、私が探しているものです。スタックアナライザーは私にはやり過ぎのように見えます。

ADAに存在するものと同様のもので問題ありません。gnatマニュアルからこのマニュアルページを見てください:

22.2静的スタック使用状況分析

-fstack-usageを使用してコンパイルされたユニットは、関数ごとに、使用されるスタックの最大量を指定する追加のファイルを生成します。このファイルのベース名は、.su拡張子の付いたターゲットオブジェクトファイルと同じです。このファイルの各行は、次の3つのフィールドで構成されています。

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

2番目のフィールドは、関数フレームの既知の部分のサイズに対応します。

修飾子staticは、関数のフレームサイズが純粋に静的であることを意味します。これは通常、すべてのローカル変数のサイズが静的であることを意味します。この場合、2番目のフィールドは、関数スタックの使用率の信頼できる測定値です。

修飾子動的とは、関数のフレームサイズが静的ではないことを意味します。これは主に、一部のローカル変数のサイズが動的である場合に発生します。この修飾子が単独で表示される場合、2番目のフィールドは関数スタック分析の信頼できる尺度ではありません。有界で修飾されている場合、2番目のフィールドが関数スタック使用率の信頼できる最大値であることを意味します。

于 2008-09-24T10:58:06.273 に答える
4

静的コード分析でこれに十分な数値が得られなかった理由がわかりません。

特定の関数ですべてのローカル変数を見つけるのは簡単です。各変数のサイズは、C 標準 (組み込み型の場合) または計算によって (構造体や共用体などの複雑な型の場合) 見つけることができます。

確かに、答えが 100% 正確であるとは限りません。コンパイラは、パディング、レジスタへの変数の配置、不要な変数の完全な削除など、さまざまな種類の最適化を実行できるためです。しかし、それが与える答えは、少なくとも適切な見積もりでなければなりません。

Google で簡単に検索してStackAnalyzerを見つけましたが、他の静的コード分析ツールにも同様の機能があると思います。

100%正確な数値が必要な場合は、コンパイラからの出力を確認するか、実行時にチェックする必要があります(ラルフが返信で提案したように)

于 2008-09-24T10:44:15.560 に答える
1

組み込みプラットフォームを使用していると仮定すると、ツールチェーンがこれに対応していることに気付くかもしれません。優れた商用組み込みコンパイラ (たとえば、Arm/Keil コンパイラなど) は、多くの場合、スタック使用量のレポートを作成します。

もちろん、割り込みと再帰は通常、それらを少し超えていますが、誰かがスタックのどこかに数メガバイトのバッファでひどい失敗を犯したかどうかを大まかに知ることができます.

于 2008-09-24T11:03:48.647 に答える
1

すべてのものをまとめるのはコンパイラーだけなので、実際に知っているのはコンパイラーだけです。生成されたアセンブリを調べて、プリアンブルにどれだけのスペースが予約されているかを確認する必要がallocaありますが、実行時にどちらが処理を行うかなどは実際には考慮されていません。

于 2008-09-24T08:34:48.663 に答える
1

正確には「コンパイル時」ではありませんが、ビルド後のステップとしてこれを行います。

  • リンカーにマップ ファイルを作成させる
  • マップ ファイル内の各関数について、実行可能ファイルの対応する部分を読み取り、関数のプロローグを分析します。

これは StackAnalyzer が行うことと似ていますが、はるかに単純です。実行可能ファイルまたは逆アセンブリを分析することが、コンパイラの出力に到達する最も簡単な方法だと思います。コンパイラはこれらのことを内部的に認識していますが、残念ながらそれを取得することはできません (コンパイラ ベンダーに機能の実装を依頼するか、オープン ソース コンパイラを使用している場合は、自分で行うか、誰かに任せることができます)。あなたのために)。

これを実装するには、次のことが必要です。

  • マップファイルを解析できる
  • 実行可能ファイルの形式を理解する
  • 関数プロローグがどのように見えるかを知り、それを「デコード」できる

これがどれほど簡単か難しいかは、ターゲット プラットフォームによって異なります。(組み込み?どの CPU アーキテクチャ?どのコンパイラ?)

これらはすべて x86/Win32 で確実に実行できますが、このようなことをしたことがなく、すべてをゼロから作成する必要がある場合は、完了して何かが機能するまでに数日かかることがあります。

于 2008-09-24T11:39:20.637 に答える
0

一般的ではありません。理論的なコンピューター サイエンスにおける停止問題は、一般的なプログラムが特定の入力で停止するかどうかを予測することさえできないことを示唆しています。一般に、プログラムの実行に使用されるスタックを計算すると、さらに複雑になります。だから:いいえ。特別な場合かもしれません。

再帰レベルが任意の長さの入力に依存する再帰関数があり、すでに運が悪いとしましょう。

于 2008-09-24T09:43:05.597 に答える