web-dev-qa-db-ja.com

C ++を使用したLCOV / GCOVブランチカバレッジにより、至る所でブランチを生成

LCOV/GCOVを使用して、プロジェクトのテストカバレッジを作成しています。最近、ブランチカバレッジをさらに有効にしようとしました。しかし、どうやら、これは開発者の高レベルのビューから期待する結果をもたらさないようです。

C++でブランチカバレッジを使用すると、レポートが至る所にブランチで広がります。 (問題の検索が示すように)ほとんどの場合、例外処理コードがこれらの「隠されたブランチ」を作成すると思われます。また、GCOV/LCOVはこれらをスキップしないようです。

問題を示す小さなテストプロジェクトを作成しました: https://github.com/ghandmann/lcov-branch-coverage-weirdness

現在、Ubuntu 16.04を使用しています。で:

  • gcc v5.4
  • lcov genhtml v1.12

本番コードは、c ++ 11を有効にして構築されています。最小限の例はc ++ 11を有効にして構築されていませんが、さまざまなオプション(c ++標準、最適化、-fno-exceptions)私たちはまずまずの結果を思いつきませんでした。

誰かアイデアがありますか?ヒント?私たちは何か間違った方法で使用していますか?これは-他のどこかで述べたように-本当に期待される動作ですか?

更新:

gcc-helpメーリングリスト でも指摘されているように、これらの「隠されたブランチ」は例外処理のために発生します。したがって、-fno-exceptions gccに切り替えると、「単純な」プログラムに対して100%の分岐カバレッジが生成されます。しかし、例外が無効になっている場合、gccは実際に例外を使用するコード(try-catch、throwなど)のコンパイルを拒否します。したがって、実際の製品コードでは、これはオプションではありません。どうやら、この場合は新しい100%になるように〜50%のカバレッジを宣言する必要があります。 ;)

19
ghandi

問題は、GCCは、スローされた例外が原因でスコープが終了する可能性がある各行のブランチ情報も記録することです(たとえば、GCC 6.3.1およびlcov 1.12を使用したFedora 25上)。

この情報の価値は限られています。ブランチカバレッジデータの主な使用例は、次のような複数節の論理式を持つ複雑なif文です。

if (foo < 1 && (bar > x || y == 0))

テストスイートがbar > xのケースもカバーしているかどうか、またはy == 0のテストケースだけがあるかどうかを確認したいとします。

これには、ブランチカバレッジデータの収集とlcovのgenhtmlによる視覚化が役立ちます。次のような単純なifステートメントの場合

if (p == nullptr) {
  return false;
}
return true;

次の行のカバレッジを調べることで、ブランチが取得されたかどうかがわかるため、ブランチカバレッジデータは必要ありません。

genhtmlによって生成されるlcovの入力は、比較的単純なテキスト形式です( geninfo(1) を参照)。したがって、BRDA:で始まりif文に属さないすべての行が削除されるように、後処理できます。たとえば、このアプローチを実装する filterbr.py を参照してください。他のlcov/genhtml処理ステップについては gen-coverage.py を、参照してください 結果のトレースファイルがcodecovにアップロードされるプロジェクト例 (codecovはgenhtmlを使用しませんlcovトレースファイルをインポートして、ブランチカバレッジデータを表示できます)。

(非)代替案

  • 例外を無効にすることは、C++コードが何も使用しない場合の唯一のオプションです
  • -O1 -fno-omit-frame-pointer -fno-optimize-sibling-callsのようなものでコンパイルすると、記録されたブランチカバレッジデータの数はいくらか減りますが、それほど多くはありません。
  • ClangはGCOVスタイルのカバレッジコレクションをサポートしますが、 'source-based code coverage'-fprofile-instr-generate -fcoverage-mappingでコンパイルし、llvm-profdataおよびllvm-covでポストプロセス)と呼ばれる別のアプローチも実装します。ただし、そのツールチェーンはブランチカバレッジデータをサポートしていません(2017-05-01現在)。
  • デフォルトでは、lcov + genhtmlはブランチカバレッジデータを生成しません-場合によっては実際には必要ありません(上記を参照)。したがって、それを無効にするオプションです(--rc lcov_branch_coverage=0および--no-branch-coverage
12
maxschlepzig

GCCは、多数の例外処理を追加します。特に関数呼び出しを行うとき。

これを修正するには、ビルドに-fno-exceptions -fno-inlineを追加します。

追加する必要があります。おそらく、テストのためにこれらのフラグをオンにするだけです。だからこのようなもの:

g++ -O0 --coverage -fno-exceptions -fno-inline main.cpp -o test-coverage 
3
zsnafu

g++ -O3 --coverage main.cpp -o testcovを試すことができます。私はあなたのファイルでg ++-5.4でこれを試しましたが、正常に動作します。つまり、標準のprintfと文字列呼び出しで例外が破棄されます。

実際、O0以外の最適化フラグを使用すると、gcovはCPPファイル内の単純な標準ライブラリ呼び出しに対して生成された例外を無視します。通常の例外も最適化されるかどうかはわかりません(そうは思いませんが、まだ試していません)。

しかし、プロジェクトでO1O2O3、さらにはOsではなく、O0のみをコードで使用する必要があるかどうかはわかりません。 。

私はちょうど同じ問題に出くわしたので、例外のためにこれらのカバーされていないブランチを取り除きたいです。私に適した解決策を見つけました。

コードで「例外をスロー」を使用することは避けます。代わりに例外をスローするメソッドを提供するクラスを設計しました。例外クラスはそれほど複雑ではないため、カバレッジについてはあまり気にしません。そのため、LCOV_EXCL_STARTおよびLCOV_EXCL_STOPですべてを除外します。または、その例外クラスのブランチカバレッジのみをオフにすることもできます。

私は認めますが、それは簡単な解決策ではありませんが、私の目的のためには、他の理由のためにも完璧です(その例外クラスが柔軟である必要がありますので、異なる実装を提供できるようになります:そうしないと)。

0
tangoal