フロー解析の深さの設定
フロー解析エンジンは、解析されたコードからパスを分析し、さまざまな問題を検出します。アプリケーション全体にわたって「実行される可能性があるパス」をすべて解析するのは現実的ではない場合もあるため、解析レベルの深さを設定できます。解析レベルが深いほど、発見される違反の数が増えます。ただし、パフォーマンスが低下し、若干メモリの消費量が増加します。
解析の深さは、DTP でテスト コンフィギュレーション インターフェイスを使用して設定します。 Report Center > [テスト コンフィギュレーション] > [静的解析] > [フロー解析詳細設定] > [パフォーマンス] > [解析の深さ] にアクセスし、以下のいずれかのオプションを選択します。
- 最も浅く (最も高速): ソース コード中の最も明らかな問題だけを発見します。発見する対象は、問題が発生しているコード箇所に「問題の起点」が近い場合だけに制限されます。発見される違反の実行パスは通常、1 つの関数のいくつかの行にわたります。4 以上の関数呼び出しにわたることはまずありません。
- 浅く (高速): [最も浅く] オプションと同様、ソース コード中の最も明らかな問題だけを発見します。ただし、[浅く] オプションは [最も浅く] オプションよりも多くの問題を検出し、より長いパスを検証できます。
- 標準: 何十もの要素を含む実行パスにおける、多くの複雑な問題を発見します。[標準] オプションは、[浅く] オプションよりも深い解析を実行するのに加えて、より複雑な問題を探します。たとえば、「不正なフローのために1 つの関数で発生する問題」や、「解析対象プロジェクト中の異なる箇所にある異なる関数間で不適切なやり取りがあるために発生する問題」などです。多くの場合、[標準] オプションが発見する違反は、解析対象ソース コード中の重要なバグを明らかにし、そのコード行は何十行にも及びます。
- 深く (低速): [標準] オプションで定義されているのと同等の複雑さと性質の問題を、より多く検出できます。ただし、解析速度は [標準] オプションよりも遅くなります。
- 徹底的 (より低速): より複雑な問題を発見します。コードベースを徹底的に解析するため、時間を必要とします。しかし、アプリケーション中の異なる場所にある何百行というコードに違反パスがわたるような、非常に複雑な問題を数多く発見します。このオプションは夜間のテスト実行で使用することを推奨します。
デフォルトの解析レベルの深さは [標準] です。
タイムアウトの方針の設定
解析レベルの深さの他に、フロー解析エンジンはタイムアウト設定を利用して、妥当な時間内で解析が終了するようにします。タイムアウトの設定を行うには、DTP でテスト コンフィギュレーション インターフェイスを使用します。Report Center > [テスト コンフィギュレーション] > [静的解析] > [フロー解析詳細設定] > [パフォーマンス] > [タイムアウトの方針] にアクセスし、以下のいずれかのオプションを選択します。
- 時間: 指定時間が過ぎると、指定したホットスポットの解析が停止します。 注意:このオプションを選択すると、レポートされる違反件数がやや不安定になる場合があります。
- インストラクション: フロー解析エンジンのインストラクションの実行回数が指定の回数を超えると、指定したホットスポットの解析が停止します。注意: 環境に合った適切なインストラクション数を決定するには、生成されたレポートの [セットアップの問題] セクションでタイムアウト情報を確認してください。
- オフ: タイムアウトしません。注意: このオプションを選択すると、解析が終了するまで時間がかかる場合があります。
デフォルトのタイムアウト オプションは [時間] であり、タイムアウト秒数は 60 秒です。解析中に発生したフロー解析タイムアウトについての情報を得るには、解析後に生成されるレポートで [セットアップの問題] セクションを確認してください。
解析データのスワップを有効にしたフロー解析の実行
スワップ モードでは、解析データをハードディスクに書き込みます。解析データのスワップは、インクリメンタル解析と同じ永続性記憶装置を使用し、インクリメンタル解析と似たプロセスで実行されます。大きなプロジェクトで解析を実行する場合、解析するソース コードの意味論的モデルを表す解析データが、フロー解析が使用できるすべてのメモリを消費する可能性があります。この現象が発生した場合、フロー解析は現在必要ない解析データをメモリから削除し、後でディスクから再び読み込みます。
一般的に、 Xmx JVM オプションを使って JVM ヒープのサイズを大きくしてを実行することを推奨します。これはスワップを最小限にするためであり、結果としてパフォーマンスが向上します。充分なメモリがある場合、解析データのスワップは無効化しても構いません。スワップを無効化すると、コード解析の速度が上がる場合があります。
スワップ モードを有効化/無効化するには、DTP のテスト コンフィギュレーション画面を使用します。
ディスクへの解析データのスワップを有効化: デフォルトで 無効 です。大量のメモリを必要としない小さなサイズのプロジェクトに対してフロー解析を実行する場合、または 64-bit システムのように十分なメモリがある場合、このオプションを無効化すると、解析のパフォーマンスが向上する場合があります。
フロー解析の冗長性の設定
DTP のテスト コンフィギュレーション画面では、次のオプションを設定できます。
- 原因が特定できない場合は違反をレポートしない: 原因を表示できない場合に違反をレポートするかどうかを指定します。
一部のフロー解析ルールでは、フロー解析はあるポイントに至るすべての可能なパスをチェックし、すべてのパスについて特定の条件が満たされることを検証する必要があります。そのような場合、1 つの違反が複数のパスに関連付けられます (単純なケースでは、1 つの違反が 1 つのパスによって表されます)。そのような違反のすべてのパスは、違反のすべてのパスに共通する違反ポイントで終わります。ただし、別のパスがコード中の別のポイントで開始する可能性もあります。各パスの開始点が違反の原因 (コード中のポイント。それよりも後の違反ポイントのコードで特定の条件の違反を規定する) です。「複数パスの違反」でパスによって原因が異なる場合、フロー解析は違反のポイントだけを表示します (違反の原因は表示しません)。
違反の原因から違反ポイントまでの完全なパスが表示される通常のケースと比較すると、違反ポイントだけの違反は、理解しにくい場合があります。そのため、違反の原因を表示できない場合に違反を非表示にするこのオプションが用意されています。 - 疑わしいポイントごとに違反を 1 つだけ表示: 1 つの疑わしいポイントにつき、( 1 つのルールに対する) 1 つの違反だけをレポートします。たとえば、null 値のソースが複数ある null 間接参照の可能性が検出された場合、1 つの違反だけがレポートされます。このオプションを選択すると、 フロー解析のパフォーマンスが若干速くなります。
null チェック メソッド
[null チェック メソッド] オプションを指定すると、メソッドに null パラメーターが渡されたときに返す期待値を指定できます。そのため、偽陽性が減少するほか、null 変数の値が不明なときに通常作成される過剰なパスが減少します。
[有効] チェックボックスをオンにして、以下の情報を指定します。
- 完全修飾型名(ワイルドカード): メソッドを含む型の完全修飾名を指定します。
- メソッド名: メソッド名を指定します。
- Null の場合の戻り値: メソッドに null パラメーターが渡されたときに返される値を指定します。
- + サブクラスの定義: サブクラス中の null チェック メソッドの定義も、null チェック メソッドとして見なすかどうかを指定します。
null チェック メソッドの例
フロー解析は解析スコープでメソッドを解析します。 たとえばサード パーティのライブラリなど、現在の解析スコープの外にメソッドがある場合、null チェック メソッドでパラメーターを指定する必要があります。この例のクラス Account
および AccountManager
は、別のアセンブリに定義されており、解析スコープの外にあります。AccountManager
クラスは、静的メソッドを使って次のように Account
クラスをチェックします。
public class Account { public int Balance { get; set; } } public class AccountManager { public static bool IsNullOrEmpty(Account account) { return account == null || account.Balance == 0; } }
そして、フロー解析の ルール BD.EXCEPT.NR ルールによって解析される次のクラスがあります。
public class SomeClass { void SomeMethod() { Account account = null; //some other actions if (!AccountManager.IsNullOrEmpty(account)) { Console.WriteLine(account.Balance); } } }
フロー解析は メソッド IsNullOrEmpty()
の戻り値が不明であると仮定します。その理由は、このメソッドは解析スコープ中にないからです。フロー解析は if ブランチを解析し、それらの文の 1 つで違反を発見します。しかし、変数が null の場合、IsNullOrEmpty()
メソッドの呼び出しは true を返します。
null チェック メソッドのメソッド リストに AccountManager.IsNullOrEmpty()
を追加して戻り値を指定することで、追跡された変数がこのメソッドに渡されたときにフロー解析は違反をレポートしなくなります。なぜなら、if
ブランチが解析されず、その結果として偽陽性が回避されるからです。
null チェック メソッドの制限事項
null チェック メソッドのパラメーターに追加されるメソッドは、単純な Bool 型の戻り値を持つ、static メソッドであるべきです。さらに、null チェック メソッドは入力パラメーターを 1 つだけ持つことができます。 この制限事項は、パラメーター指定が複雑になり過ぎることを防ぎ、null チェック メソッドの結果に影響しかねない変数が他にないことを保証します。
リソースの指定
[リソース] タブでは、BD.RES (リソース) カテゴリのルールを使ってチェックするリソースを定義することができます。BD.RES カテゴリのルールは、[リソース] タブで定義および有効化されているすべてのリソースについて、リソースが正しく使用されているかをチェックします。
- リソースのタイプを指定します。
- [有効] チェックボックスをオンにします。
- 適切な場合、[アプリケーション終了時の違反をレポートしない] オプションをオフにします。
矢印をクリックして [リソース アロケーター] および [リソース クローザー] タブを開き、表示されたテーブルにリソースのアロケーター/クローザーの情報を入力します。詳細については以下のセクションを参照してください。
リソース アロケーターの設定
[リソース アロケーター ] テーブルには、リソースを生成するメソッドの記述を入力できます。テーブルには以下の列があります。
- 有効: 解析中にアロケーターを考慮するかどうかを指定します。
- 完全修飾型名または名前空間 (ワイルドカード): メソッドが宣言された型または名前空間の完全修飾名です。任意の型 (または名前空間) で宣言された関数、また型や名前空間の外で宣言されたグローバル関数を指定するには '*' を使用します。
- メソッド名 (ワイルドカード): アロケーター メソッドの名前を指定します。任意の数の任意の文字を表すには '*' を使用します。
- リソース パラメーター: メソッドが リソースを割り当てる1つ以上のパラメーターを指定します。パラメーターの位置 (1 から開始) を指定するか、アスタリスク (*) を指定して、すべてのパラメーターにリソースを割り当てます。
- + サブクラスの定義: サブクラス中の (指定された名前の) アロケーター メソッドの定義も、アロケーター メソッドとして見なすかどうかを指定します。この設定は、インスタンス メソッドと非インスタンス メソッドの両方に適用されます。
- "this" オブジェクトがリソース: メソッドが呼び出されたオブジェクトのリソースを割り当てることを示します。
- リソース オブジェクトを返す: メソッドが割り当てられたリソースを返すことを示します。
一般的に、アロケーター メソッドはエラー コードを返してリソース割り当ての失敗を表します。アロケーターメソッドはリソースを返し、通常 NULL 値はリソース割り当ての失敗を表します。リソース リークを探すときに、フロー解析はリソースの割り当てが成功したか失敗したかを把握できる必要があります。これは、割り当てが実際に発生したパス上で発見されない割り当て解除メソッドへの呼び出しだけをフロー解析がレポートするのに役立ちます。リソース アロケーターメソッドがリソースを返し、NULL でない場合、フロー解析はリソースが正常に割り当てられたと推測します。
リソース クローザーの設定
[リソース クローザー] テーブルには、リソースをクローズするメソッドの記述を入力できます。テーブルには以下の列があります。
- 有効: 解析中にクローザーを考慮するかどうかを指定します。
- 完全修飾型名または名前空間 (ワイルドカード): メソッドが宣言された型または名前空間の完全修飾名です。任意の型 (または名前空間) で宣言された関数、また型や名前空間の外で宣言されたグローバル関数を指定するには '*' を使用します。
- メソッド名 (ワイルドカード): クローザー メソッドの名前を指定します。任意の数の任意の文字を表すには '*' を使用します。
- + サブクラスの定義: サブクラス中の (指定された名前の) クローザー メソッドの定義も、クローザー メソッドとして見なすかどうかを指定します。この設定は、インスタンス メソッドと非インスタンス メソッドの両方に適用されます。
- "this" オブジェクトが リソース: メソッドが呼び出されたオブジェクトのリソースをクローズすることを示します。
- リソース パラメーター: メソッドが リソースをクローズする 1つ以上のパラメーターを指定します。パラメーターの位置 (1 から開始) を指定するか、アスタリスク (*) を指定して、すべてのパラメーターにリソースを割り当てます。
常に解析されるメソッドの指定
[常に解析すべきメソッド] オプションを使用すると、実行パス中で見つかったとき常に解析するメソッドを定義できます。これにより、特定のパスを解析したとき、通常は入らないメソッドを必ず解析するようにできます。
[有効] チェックボックスをオンにし、テーブルに以下の情報を入力します。
- 完全修飾型名(ワイルドカード) メソッドを含む型の完全修飾名を指定します。
- メソッド名: メソッド名を指定します。
- + サブクラスの定義: サブクラス中のメソッドも考慮するかどうかを指定します。
複数のプロジェクトがあるソリューションの分析
複数のプロジェクトから構成される大規模なソリューションを分析すると、メモリ使用量が増加し、パフォーマンスが低下する可能性があります。システムのメモリが不足した場合は、完了前にフロー解析がフリーズすることがあります。フロー解析が正常に完了するようにするには、テスト コンフィギュレーションの .properties ファイルで次のオプションを指定して、個々のプロジェクトを個別に分析するように dotTEST を構成します。
flowanalysis.analyze.project.by.project=true
これにより、メモリ消費を大幅に削減し、パフォーマンスを向上させ、フロー解析が完了する前にフリーズするのを防ぐことができます。
このオプションを有効にすると、dotTEST は個々のプロジェクトを解析しますが、プロジェクト間の解析は実行しません。このため、flowanalysis.analyze.project.by.project
オプションが無効の場合、解析結果は、同じスコープでレポートされた結果と矛盾することがあります。