tcov は、プログラムの実行方法に関する行単位の情報を提供します。tcov はソースファイルのコピーを作成し、そこにどの行がどれくらいの頻度で使用されているかを示す注釈を挿入します。また、基本的なブロックについての情報も集約します。tcov は時間ベースのデータは生成しません。
tcov を利用するための手順は以下の 3 段階となっています。
tcov の実験データ作成用に、プログラムをコンパイルします。
実験を実行します。
tcov を使って、プログラムの各ステートメントの実行カウントの集計を生成します。
コードカバレージ用にプログラムをコンパイルするには、C コンパイラに対して -xa オプションを使用します。index.assist という名前のプログラムを例として使用する場合、以下のコマンドによって、tcov 用のコンパイルを行ってください。
% cc -xa -o index.assist index.assist.c
C++ または f77 コンパイラに対しては、-a コンパイラオプションを使用します。
C コンパイラは index.assist.c に存在する基本ブロックについてのデータベースエントリを含む index.assist.d ファイルを生成します。プログラム index.assist が実行され、終了した時点で、コンパイラは index.assist.d ファイルを更新します。
tcov は C および C++ プログラムとは動作しますが、#line または #file 指示が含まれるファイルはサポートしません。tcov は、#include ヘッダーファイルのコードのテストカバレージ解析も行うことができません。-xa (C)、-a (その他のコンパイラ)、+d (C++) オプションを指定してコンパイルされたアプリケーションは通常よりも実行速度が遅くなります。+d オプションは C++ のインライン関数の拡張を禁止するため、各実行時の .d ファイルの更新にかなりの時間がかかります。
index.assist.d ファイルは、環境変数 TCOVDIR が指示するディレクトリに作成されます。TCOVDIR が設定されていない場合、index.assist.d はカレントディレクトリに作成されます。
index.assist.c のコンパイルが済んだら、index.assist を実行してください。
% index.assist % ls *.d index.assist.d
それでは、tcov を実行して、プログラムの各ステートメントの実行カウントの集計結果が含まれるファイルを生成してください。tcov は index.assist.d ファイルを使って、コードの注釈リストを含む index.assist.tcov ファイルを生成します。この出力には、各ソースステートメントが実行された回数が示されます。ファイルの末尾には、短い要約が付いています。
% tcov index.assist.c % ls *.tcov index.assist.tcov
次に、index.assist のモジュールの1つから、C コードの一部分 を示します。問題となっているモジュールは、呼び出し頻度の高い insert_index_entry 関数です。
struct index_entry * 11152-> insert_index_entry(node, entry) structindex_entry *node; struct index_entry *entry; { int result; int level; result = compare_entry(node, entry); if (result == 0) { /* exact match */ /* Place the page entry for the duplicate */ /* into the list of pages for this node */ 59 -> insert_page_entry(node, entry->page_entry); free(entry); return(node); } 11093-> if (result > 0) /* node greater than new entry -- */ /* move to lesser nodes */ 3956-> if (node->lesser != NULL) 3626-> insert_index_entry(node->lesser, entry); else { 330-> node->lesser = entry; return (node->lesser); } else /* node less than new entry -- */ /* move to greater nodes */ 7137-> if (node->greater != NULL) 6766-> insert_index_entry(node->greater, entry); else { 371-> node->greater = entry; return (node->greater); } }
C コードの左の数値は、各ステートメントが実行された回数を表しています。insert_index_entry 関数は 11,152 回呼び出されます。
tcov は、index.assist.tcov のファイルの末尾に以下のような集計情報を追加します。
Top 10 Blocks Line Count 240 21563 241 21563 245 21563 251 21563 250 21400 244 21299 255 20612 257 16805 123 12021 124 11962 77 Basic blocks in this file 55 Basic blocks executed 71.43 Percent of the file executed 439144 Total basic block executions 5703.17 Average executions per basic block
コードカバレージ解析用にコンパイルされたプログラムは、(異なる入力を基にしての) 繰り返し実行が可能です。つまり、プログラムに対して tcov を繰り返し使用し、各実行時の動作を比較できるということです。
tcov によってプロファイル化された共有ライブラリを生成し、バイナリファイルのリンクがすでに済んでいても、それを代わりに使用することが可能です。共有可能なライブラリを生成する場合は、-xa オプション (C) または -a オプション (その他のコンパイラ) を使用してください。以下に例を示します。
% cc -G -xa -o foo.so.1 foo.o
このコマンドは、共有可能なライブラリに tcov プロファイリングサブルーチンのコピーを組み込み、ライブラリのクライアントが再リンクしなくても済むようにします。ライブラリのクライアントもまたプロファイリング用にリンクされる場合、クライアントによって使用される tcov サブルーチンのバージョンは、共有可能ライブラリのプロファイルに使用されます。
tcov は、.d ファイルのブロックカバレージデータベースの更新に、シンプルなファイルロッキングメカニズムを使用します。tcov はこの目的のために単独ファイル /tmp/tcov.lock を使用します。そのため、システム上では、-xa (C) または -a (その他のコンパイラ) を使ってコンパイルされたただ 1 つの実行可能ファイルが実行されなければなりません。-xa または -a オプションを使ってコンパイルされたプログラムの実行は、手動で終了し、その後、/tmp/tcov.lock ファイルも手動で削除する必要があります。
プログラムが tcov によるプロファイリング用にリンクされると、-xa または -a オプションを使ってコンパイルされたファイルは自動的にプロファイリングツールのサブルーチンを呼び出します。プログラムの終了時点で、これらのサブルーチンは、ファイル xyz.f の実行時に収集された情報と、ファイル xyz.d に保存された既存のプロファイリング情報とを組み合わせます。プロファイル化されたバイナリファイルを同時に実行している人々にこの情報を変更されないようするため、更新期間中は xyz.d 用にロックファイル xyz.d.lock が作成されます。xyz.d またはそのロックファイルのオープンまたは読み取り時にエラーが存在する場合、あるいは、実行時の情報と保存されている情報との間に不整合が存在する場合、xyz.d に保存されているデータは更新されません。
xyz.d の編集または再コンパイルは、xyz.d のカウンタの数を変える可能性があります。古いプロファイル化されたバイナリの実行の際に、こうした変化が検出されます。
プロファイル化されたバイナリを実行する人が多すぎると、ロックが得られない可能性もあります。その場合は、数秒の遅れの後に、次のようなエラーメッセージが表示されます。
tcov_exit: Failed to create lock file '/tmp_mnt/net/rbbb/export/home/src/newpattern/foo.d.lock' for coverage data file '/tmp_mnt/net/rbbb/export/home/src/newpattern/foo.d' after 5 tries. Is somebody else running this binary?
そして、保存されている情報の更新は行われません。このロックはネットワークを通じて安全に機能します。ロックはファイル単位で行われるため、ほかのファイルは正しく更新されます。
プロファイリングサブルーチンは、アクセス不可能となっている自動マウントファイルシステムを処理しようと試みます。それでも、異なるマシン上で、カバレージデータファイルを含むファイルシステムに異なる名前がマウントされていたり、プロファイル化されたバイナリを実行するユーザーがカバレージデータファイルまたはそれを含むディレクトリに対する書き込み権を持っていない場合、この試みは失敗します。すべてのディレクトリには、統一化された名前を付け、バイナリを実行する可能性のあるユーザー誰もが書き込み可能な状態しておいてください。
以下のエラーメッセージが、tcov ランタイムルーチンによって報告されることがあります。
tcov_exit: Could not open coverage data file 'coverage data file name' because 'system error message string'.
このバイナリを実行するユーザーには、カバレージデータファイルに対する読み取り権および書き込み権がありません。カバレージデータファイルを含むディレクトリが削除されている場合も、こうした問題が起こります。
tcov_exit: Could not write coverage data file 'coverage data file name' because 'system error message string'.
このバイナリを実行するユーザーには、カバレージデータファイルを含むディレクトリに書き込み権がありません。カバレージデータファイルを含むディレクトリがバイナリを実行するマシンにマウントされていない場合も、こうした問題が起こります。
tcov_exit: Failed to create lock file 'lock file name' for coverage data file 'coverage data file name' after 5 tries. Is someone else running this executable?
多くのユーザーが同時にカバレージデータファイルを更新しようとしています。カバレージデータファイルが更新されている間に、マシンがクラッシュしたときも、ロックファイルが放置され、こうした問題は起こります。万一、クラッシュが起こった場合には、2 つのファイルのうちで長い方をクラッシュ後のカバレージデータファイルとして使用してください。ロックファイルは手動で削除してください。
tcov_exit: Stdio failure, probably no memory left.
利用可能なメモリがなく、標準 I/O パッケージが動作できません。この時点では、カバレージデータファイルの更新はできません。
tcov_exit: Coverage data file path name too long (length characters) 'coverage data file name'.
ロックファイル名は、カバレージデータファイル名にさらに 6 文字追加されます。そのため、派生ロックファイル名が規則違反となる可能性があります。
tcov_exit: Coverage data file 'coverage data file name' is too short. Is it out of date?
tcov プロファイル用のライブラリまたはバイナリが、同時に実行、編集、再コンパイルされようとしています。古いバイナリは一定サイズのカバレージデータファイルを予期しますが、編集によってそのサイズはしばしば変更されます。古いバイナリが古いカバレージデータファイルを更新しようとしているときに、コンパイラが新しいカバレージデータファイルを作成すると、バイナリファイルは空白、あるいは壊れた状態で表示されます。