メインコンテンツ

プログラムのスタック使用量の判断

Polyspace® Code Prover™ 解析は、プログラム全体とプログラム内の各関数のスタック使用量を見積もることができます。この解析では、プログラムの関数呼び出し階層を使用してスタック使用量を推定します。

  • 個別の関数のスタック使用量は、関数内のローカル変数のサイズの合計に関数呼び出し先の最大スタック使用量を足したものです。

  • プログラム全体のスタック使用量は、呼び出し階層最上位の関数のスタック使用量です。通常は、main() 関数が呼び出し階層の最上位にあります。

たとえば、この呼び出し階層の場合、func のスタック使用量は func に含まれるローカル変数のサイズに func1 および func2 の最大スタック使用量を足したものです (これらの変数が条件付きステートメントの相互に排他的な分岐で呼び出される場合を除く)。

A snapshot of the Call Hierarchy pane showing the function 'func' with two callees, 'func1' and 'func2'.

詳細については、以下を参照してください。

スタック使用量の計算

スタック使用量を計算するには、Code Prover 解析を開始する前に必要なオプションを有効にします。

  • Polyspace デスクトップ UI の [構成] ペインで、[Code Prover 検証] > [チェック動作] ノードを選択します。[スタック使用量の計算] チェック ボックスをオンにします。

  • コマンド ラインで、オプション [スタック使用量の計算] (-stack-usage) を使用します。

詳細については、スタック使用量の計算を参照してください。

解析を実行すると、Code Prover がスタック使用量メトリクスおよびその他の結果を報告します。[結果のリスト] ペインで結果をファミリ別にグループ化する場合は、スタック使用量メトリクスが [コード メトリクス] ノードの下に表示されます。

考えられるスタック オーバーフローの調査

使用可能なスタック領域をスタック使用量が超えている場合、原因である関数を特定できます。main() 関数から始め、プログラムの呼び出しツリーを移動します。移動中に、大きいサイズのローカル変数を報告する関数を探します。そのような関数を特定できない場合は、長い呼び出し順序を探します。移動の手順は次のとおりです。

  1. [ソース] ペインで main() 関数を選択します。[呼び出し階層] ペインに、main() から呼び出される関数 (呼び出し先) が表示されます。階層全体を表示するには、関数を右クリックしてすべてのノードを展開します。

    [呼び出し階層] ペインが既定で開いていない場合、[ウィンドウ][ビューの表示/非表示][呼び出し階層] を選択します。

    A snapshot of the Call Hierarchy pane showing all functions called directly or indirectly from the 'main' function. Double-click the function 'func2' to navigate to its definition in the source code.

  2. ソース内の呼び出し先の定義に移動するには、[呼び出し階層] ペインで呼び出し先名をそれぞれダブルクリックします。その後、[ソース] ペインで呼び出し先名をクリックします。[結果の詳細] ペインに呼び出し先のローカル変数サイズのより高い推定値とスタック使用量が表示されます。

    A snapshot of the Result Details pane showing all code metrics for the function 'func2' including its maximum stack usage,

スタック使用量が計算されない

Code Prover が関数のスタック使用量を計算するためには、解析が関数の最後に到達できる必要があります。以下の場合、関数のスタック使用量は計算されません。

  • レッド チェック。

    Code Prover はレッド チェック以降のコードを解析しないため、関数またはその呼び出し先のいずれかで明確なランタイム エラーが発生すると、関数のスタック使用量が計算されません。未解析のコードに関数呼び出しが含まれる場合、呼び出し元関数のスタック使用量の推定値は不正確になります。

    この例では、関数 func() にレッド オーバーフローが含まれています。Code Prover は、ランタイム エラー以降のコードを解析せず、func のスタック使用量を計算しません。スタック使用量が計算された場合、func2 の呼び出しなど、未解析のコードに含まれる関数呼び出しは計算に含まれていません。

    #include <limits.h>
    void func(void) {
        int val=INT_MAX;
        val++;
        func2();
    }

  • 無限ループ。

    確実に無限となるループは、通常はレッドの [無限ループ] チェックで示されます。たとえば while(1) など、ループが無限であることが自明の場合は、レッド チェックの代わりに、ループ キーワードに赤の破線の下線が示されます。キーワードのツールヒントには、このループが無限ループとして検出されたことが示されます。

  • 再帰関数。

    関数がそれ自体を直接的または間接的に呼び出す場合、Code Prover は、その関数のスタック使用量またはその関数を呼び出すすべての関数のスタック使用量を計算しません。

    再帰関数が含まれているプログラムでは、最大スタック使用量が計算されない場合でも、最小スタック使用量が計算されることがあります。この場合、プログラムの最小スタック使用量の計算では、再帰関数の呼び出しをバイパスする実行パスが使用されるため、正確なスタック使用量を表すものにはならない可能性があります。

プログラムのスタック使用量が計算されない場合は、すべての関数のスタック使用量の値が計算されることを確認してください。[結果のリスト] ペインの [情報] 列で、関数のスタック使用量の結果に "計算されていません" という値が表示されているかどうかをチェックします。

スタック使用量の前提条件

Polyspace に指定したコードで定義されていない関数が呼び出された場合、スタック使用量の判断ではその関数呼び出しを考慮しません。言い換えれば、スタック サイズへのスタブ関数の寄与はゼロと見なされます。オプション -code-behavior-specifications を使用すると、このようなスタブ関数からのスタック サイズへの寄与を明示的に指定できます。

この前提条件は以下に適用されます。

  • 暗黙的な C++ コンストラクター。

    たとえば、この例では、myObj を定義する際に func() がクラス myClass のコンストラクターを呼び出します。スタック使用量の決定では、コンストラクターが func() の呼び出し先と見なされません。

    class myClass {std::string str;};
    
    void func() {
       myClass myObj;
    }

  • 標準ライブラリ関数や Polyspace プロジェクト内のコードに定義がないその他の関数。

    たとえば、この例では、func が標準ライブラリ関数 cos() を呼び出します。cos() の定義を提供しない限り、スタック使用量の判断では cos()func() の呼び出し先とは見なしません。

    #include <math.h>
    
    double func(double arg) {
       return cos(arg);
    }

参考

| | | | | |