web-dev-qa-db-ja.com

スタックの最大使用量を判断する方法は?

組み込み/メモリ制約システムの最適なスタックサイズを決定するために利用できる方法は何ですか?それが大きすぎると、メモリが無駄になり、他の場所で使用される可能性があります。ただし、小さすぎる場合は、このWebサイトの名前が付けられます...

ジャンプスタートの事柄を試すには:The Art of Designing Embedded Systemsthat、"経験あり、スタックの適切なサイズを計算する標準的で科学的な方法を学びます:ランダムにサイズを選び、希望します。」誰もがそれよりうまくできますか?

より具体的な例が要求されました。では、オペレーティングシステムなしで MSP430 MC をターゲットとするCプログラムで2 kB RAM IAR Embedded Workbench ツールチェーンを使用)はどうでしょうか。このIDEは、JTAGデバッガーの使用中にスタックの内容と使用法を表示できます。

43
Judge Maygarden

最も深いスタック使用量を決定する最も一般的な方法は、既知であるが異常な値でスタックメモリを初期化し、定期的に(または大規模なテスト実行の最後に)パターンが停止する場所を確認することです。

これは、IAR IDE=が使用するスタックの量を決定する方法とまったく同じです。

32
Michael Burr

質問に静的分析のタグを付けましたが、これは静的分析では解決が難しい問題です。スタックの使用は、特に再帰またはallocaを使用している場合、プログラムのランタイムプロファイルに依存します。これが組み込みプラットフォームであることを考えると、psまたはtopのようなものを実行して、アプリケーションのスタック量を確認することも難しいと思います使っている。

興味深いアプローチは、現在のスタックフレームのアドレスを使用して、使用されているスタックの量を判断することです。これを行うには、関数の引数またはローカル変数のアドレスを取得します。 main関数と、最も多くのスタックを使用していると思われる関数に対して、これを行います。その違いから、アプリケーションに必要なスタックの量がわかります。以下に例を示します(通常の高から低へのスタックの成長を想定)。

char *stack_top, stack_bottom;

int
main(int argc, char *argv[])
{
    stack_top = (char *)&argc;
    // ...
    printf("Stack usage: %d\n", stack_top - stack_bottom);
}

void
deeply_nested_function(void)
{
    int a;
    stack_bottom = (char *)&a;
    // ...
}

コンパイラでカスタム関数プロローグを指定できる場合(グラフベースのプログラムプロファイリングを可能にするために多くの場合)、すべての関数がそのような測定コードを呼び出すように調整することもできます。次に、測定機能は次のようなものになります

void
stack_measurement_function(void)
{
    int a;
    stack_bottom = min(stack_bottom, (char *)&a);
    // ...
}

私は これらのチャート を生成するために、私が説明したものと同様のアプローチを使用しました。

22

優れたソースコードの静的分析ツールを使用すると、アプリケーションのコールグラフを作成できます。それと、コンパイラによって生成されたローカル/一時ファイルの量の見積もりが与えられれば、スタック需要の控えめな見積もりを簡単に計算できます。

「良い」分析ツールとは、関連するすべてのコンパイルユニットを読み取り、直接関数呼び出しを決定でき、間接ポインターを決定でき、コンパイルユニット内で、システム全体の保守的なポイントツー分析を計算でき、ポイントツー分析を考慮したコールグラフを作成します。これにより多くのツールが不要になります。そのため、「実行時にスタックを埋めて、何が起こるかを確認する」などのアドホックメソッドが表示されます。また、コンパイラーがスタックに置くスタック要求の見積もりも必要です。すべてのタイプのストレージ要求の大きさを知るだけで、これの多くを概算できます。これは、組み込みシステムのCプログラムの場合、一般にかなり簡単に決定できます。最後に、アプリケーションに再帰呼び出しがないか、またはツールが最も深い再帰(おそらくそれを伝えることによる)のアイデアを持っていると信じる必要があります。

DMS Software Reengineering Toolkitは、Cプログラムのこれらの要件をすべて満たしています。 http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html を参照してください。コールグラフをクロールし、さまざまなサイズの見積もりを使用してスタックデマンドを計算するように構成する必要があります。

迅速な回答が必要な場合は、スタックフィルトリックを使用します。ソースコードを変更するたびに再計算できる回答が必要な場合は、静的分析アプローチが必要になります。

5
Ira Baxter

私は現在この問題に取り組んでいます-つまり、スタックサイズの分析計算です。関数呼び出しには1つ以上の引数としてインデックス付き配列があり、1つ以上の配列インデックスには関数呼び出しが含まれる可能性があるため、これは非常に再帰的なコードになります。

ただし、いくつかの実現により、複雑さをある程度緩和できます。

(1)高水準言語コンパイラーを使用する場合、各ステートメント/コード行の実行終了時のスタックポインターは、開始時と同じ場所になければなりません。 (少なくともこれは、問題が発生する可能性があることを確認するための良い規則です!)

(2)各関数またはサブルーチンの呼び出しから戻った後のスタックポインタは、呼び出し前と同じである必要があります。したがって、最大スタックサイズは、プログラム内のすべてのステートメントで、各ステートメントで到達したピークスタックサイズの最大値です。 (少なくともこれは、問題が発生する可能性があることを確認するための良い規則です!)

もちろん、ステートメントには上記の再帰的な問題を含めることができますが、少なくともプログラム全体の最大スタックサイズ要件を見つけるという問題は、結局、各ステートメントの最大スタックサイズ要件を見つけ、それらの最大値を選択することになります。

これは、呼び出されたすべての関数もコンパイルされるまで完了できません。したがって、各ステートメントのスタックサイズ(基本的には各関数呼び出しの前のピーク値と各関数呼び出しの直前の値)を記録するコンパイルされた各モジュールのファイルを生成します(関数によって引き起こされるスタックサイズへの未知の追加を除く)次に、すべての関数がコンパイルされたら、再帰ルーチンを使用してこれらのファイルを遡及的に処理し、ピークスタックサイズを決定します。

幸いなことに、再帰ルーチンを除いて、可能な最大スタックサイズ要件はプログラムフローに依存しませんが、一般的なフロー(データに依存)では、この最大スタックサイズに達することはありません。

例:関数1が関数2を呼び出し、両方のプログラムフローがデータ値Xに依存するとします。関数1がその最悪のステートメントを実行するXの範囲があり、実行されない関数2への呼び出しが含まれているとします。 Xの同じ範囲の最悪の場合のステートメントです。関数1と関数2の両方の最悪の場合を同時に使用して最大スタックサイズを計算したため、スタックサイズを過大評価した可能性があります。少なくとも私たちは安全面で誤りを犯しました。

必要に応じて、割り込みルーチンにOSスタック上の独自のスタックスペースを与えたいので、割り込みからの復帰アドレスを除いて、プログラムスタック要件に追加されません。

2
cloggervic