業務で使用しているgcovrのカバレッジレポート(html形式)が想定と違っていたため、疑問解消のためにカバレッジレポートの基となるgcovの入出力と共に調査しました。
調査する以前の知識としては、以下の通りgcov、gcovrに対する知識はほとんどなく、ブラックボックス的に使用していました。
- gcovrはgcovの出力結果を要約し、htmlやxml形式のカバレッジレポートとして出力できるツールであること
- gcovの入力ファイルは、コンパイラ「gcc」が出力するgcno、gcdaファイルであること(ファイル構成、記録内容は不明)
1. 当初の想定
gcovrの使用方法とカバレッジレポートの見方については、事前知識がなかったことから、顧客から説明された以下の内容を鵜呑みにしていました。
- 条件判定式が成立する(TRUEとなる)場合、左側にチェック(✔)が付く
- 条件判定式が不成立となる(FALSEとなる)場合、右側にチェック(✔)が付く
2. 実際のカバレッジレポート
2-1. 単一条件
単一条件については、当初の想定①②を常に満たしています。
(例1:条件判定式が成立する)
(例2:条件判定式が不成立となる)
2-2. 複合条件
複合条件については、AND判定を行う上では当初の想定①②を常に満たしています。しかし、OR判定を行う上では当初の想定①②と逆の出力結果となります。
(例1:条件判定式が成立する(AND判定))
(例2:条件判定式が不成立となる(AND判定))
(例3:条件判定式が成立する(OR判定))
※第一条件が成立しているにも関わらず、右側にチェック(✔)が付く
※第一条件が不成立となるにも関わらず、左側にチェック(✔)が付く
(例4:条件判定式が成立する(OR判定))
※第一条件が不成立となるにも関わらず、左側にチェック(✔)が付く
3. カバレッジレポートの調査
2-2章の複合条件の出力結果を見ると、OR判定における第一条件の出力結果が想定と逆になることが分かります。当初は「gcovrのバグではないか」という意見も挙がりましたが、インターネットで同様の問題が騒がれていいない、GitHubで公開されているgcovrのissuesに含まれていないことから、我々の想定が誤っているという前提で、gcovrの仕様、gcovの入出力に関する調査を行いました。
3-1. gcovrの仕様
gcovrの仕様の調査では、ユーザーガイドとgcovrのソースコードを参照しました。
ユーザーガイド内でgcovrはgcovの結果の要約を生成するツールと説明があることと、gcovとgcovrを実行する前処理(ソースコードのビルド、実行)が同じであることから、gcovrの出力結果はgcovに完全に依存していることが分かります。
これはgcovrのソースコードからも明らかです。具体的に見るべきファイル、行数は以下の表にまとめます。
ファイル名 | 行数 | 説明 |
gcovr/gcov.py | 632 | run_gcov_and_process_files関数でgcovコマンドをsubprocessとして実行し、gcovの出力結果を取得している |
gcovr/gcov.py | 227 | parse_all_lines関数でgcovの出力結果(.gcov)をパースし、ソースコードとカバレッジデータを関連付けしている |
gcovr/html_generator.py | 89, 262 | print_html_report関数でgcovの出力結果(.gcov)から生成したカバレッジデータを取得し、source_row関数でチェック(✔)/クロス(✗)を出力している |
3-2. gcovの入出力
3-2-1. 入力
gcovの入力となるgcno、gcdaファイルについての説明は、Covertureというツールを紹介しているページが参考になりました。
上記のページによると、gcno、gcdaファイルにはそれぞれ以下の情報が記録されているようです。この情報から、gcovではアークの実行回数を基にカバレッジを計算されており、条件判定式の判定結果(TRUE/FALSE)という情報は記録されていないことが分かります。
(gcno)
ソースコードの関数(C++ならメソッド)のフローグラフに関する情報が保存されています。この有向グラフは、グラフのノードである基本ブロックと、グラフのエッジであるアークで構成されます。また、基本ブロックそれぞれに対応するソースコードのファイル名とその行番号も保存されています。
(gcda)
各アークの実行回数が記録されます。
3-2-2. 出力
2-2章のOR判定が成立するケース(例3)について、gcovの出力結果を取得しました。
参照するべき箇所は、9行目の条件判定式に対するbranch情報(10~13行目)です。(gcovrのソースコードの調査より、カバレッジレポート(html)に出力されるチェック(✔)、クロス(✗)は、このbranch情報を基に生成されていることが判明しています。
このbranch情報は、各条件に対して、fallthrough、無印(throw)の順に出力されていることが分かります。
fallthroughとは、
C/C++のswitch文で、あるcaseブロックから次のcaseブロックにそのまま流れていくこと。および、そのような言語仕様。
4. 結論
gcovrのカバレッジレポート(html)に出力されるチェック(✔)、クロス(✗)の順序はfallthrough、throwの順に左から出力されます。
複合条件のOR判定においては、第一条件がTRUEとなった場合に第二条件の判定がスキップされ(throw)、FALSEとなった場合に第二条件の判定が行われる(fallthrough)ため、当初の想定と逆の出力結果となっていました。