Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1206] ファイルの概要

このコミットは、Go言語の初期のコードカバレッジツールである 6cov に新たなコマンドラインフラグを追加するものです。具体的には、カバレッジレポートにソースコードの行を表示する -s フラグと、指定した行数以下のセクションに関する出力を抑制する -n x フラグが導入されました。これにより、6cov の出力がより詳細かつフィルタリング可能になり、開発者がコードカバレッジ情報を分析する際の利便性が向上しました。

コミット

commit 4af8ef65191146a72ab36e70667f639b2946963a
Author: Russ Cox <rsc@golang.org>
Date:   Thu Nov 20 11:58:07 2008 -0800

    new flags to 6cov
    
            -s      print source lines
            -n x    don't print about sections < x lines long.
    
    R=r
    DELTA=69  (57 added, 1 deleted, 11 changed)
    OCL=19697
    CL=19708

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4af8ef65191146a72ab36e70667f639b2946963a

元コミット内容

    new flags to 6cov
    
            -s      print source lines
            -n x    don't print about sections < x lines long.
    
    R=r
    DELTA=69  (57 added, 1 deleted, 11 changed)
    OCL=19697
    CL=19708

変更の背景

この変更は、Go言語の初期開発段階において、コードカバレッジツール 6cov の機能強化を目的としています。6cov は、プログラムの実行中にどのコードが実行されたかを追跡し、テストによってカバーされていない部分を特定するために使用されていました。

従来の 6cov は、カバレッジ情報を提供するものの、その出力は必ずしも詳細ではありませんでした。特に、テストで実行されなかったコードセクションについて、その具体的なソースコードの内容や、非常に短い未実行セクションに関するノイズの多い情報が課題となっていました。

このコミットは、以下のニーズに応えるために導入されました。

  1. 未実行コードの特定と理解の向上: テストでカバーされていないコードセクションがどこにあるのかを、ソースコードと関連付けて直接表示することで、開発者が問題をより迅速に特定し、理解できるようにする。
  2. レポートのノイズ削減: 非常に短い(例えば数行の)未実行コードセクションに関する報告は、多くの場合、重要度が低く、レポート全体の可読性を低下させる可能性があります。これをフィルタリングする機能を提供することで、開発者がより重要な情報に集中できるようにする。

これらの機能追加により、6cov はより実用的なツールとなり、Go言語プロジェクトの品質向上に貢献することが期待されました。

前提知識の解説

6cov ツール

6cov は、Go言語の非常に初期の段階で存在したコードカバレッジツールです。Go言語のコンパイラやツールチェインは、ターゲットアーキテクチャに応じて 6g (Goコンパイラ for amd64), 6l (Goリンカ for amd64) のように数字と文字の組み合わせで命名されていました。6cov もその流れを汲むもので、6amd64 アーキテクチャを指していました。

現在のGo言語では、go test -covergo tool cover といった標準のツールがコードカバレッジの測定とレポート生成に用いられており、6cov は歴史的なツールとなっています。しかし、このコミットは、Go言語のコードカバレッジ機能の基礎を築く上で重要な一歩であったと言えます。

コードカバレッジ

コードカバレッジとは、ソフトウェアテストの品質を測る指標の一つで、テストが実行された際に、プログラムのソースコードのうちどれくらいの割合が実行されたかを示すものです。主なカバレッジの種類には以下のようなものがあります。

  • ステートメントカバレッジ (Statement Coverage): プログラムの各ステートメントが少なくとも一度は実行されたかどうか。
  • ブランチカバレッジ (Branch Coverage): if 文や switch 文などの条件分岐において、全ての分岐パスが少なくとも一度は実行されたかどうか。
  • ファンクションカバレッジ (Function Coverage): プログラム内の全ての関数が少なくとも一度は呼び出されたかどうか。

コードカバレッジを測定することで、テストが不十分な部分を特定し、テストケースを追加することでソフトウェアの品質と信頼性を向上させることができます。

OCL と CL

Go言語のコミットメッセージに見られる OCLCL は、Googleの内部バージョン管理システム(Perforceなど)に由来する概念です。

  • CL (Change List): 「チェンジリスト」の略で、バージョン管理システムに提出される変更のまとまりを指します。これは、Gitにおけるコミットに相当する概念です。Goプロジェクトでは、Gerritというコードレビューシステムが使われており、Gerrit上での変更単位もCLと呼ばれます。
  • OCL (Original Change List): 歴史的に、GoプロジェクトがSubversionからPerforceへ移行する際に、元のチェンジリスト番号を示すために使われたタグです。このコミットのように、初期のGoプロジェクトのコミットメッセージには、OCL=CL= の両方が含まれることがありました。これは、Goプロジェクトの初期のバージョン管理の歴史を示すメタデータの一部です。

これらのタグは、現在のGoプロジェクトのコミットメッセージでは一般的に使用されていませんが、Goの歴史的なコミットを理解する上で重要な情報となります。

技術的詳細

このコミットは、src/cmd/cov/main.c ファイルに対して行われ、6cov ツールのコマンドライン引数解析と出力ロジックを拡張しています。

新しいコマンドラインフラグ

  1. -s フラグ:
    • 目的: 未実行のコードセクションを報告する際に、関連するソースコードの行を直接出力します。
    • 実装: doshowsrc という新しいグローバル変数が導入され、-s フラグが指定された場合に 1 に設定されます。missing 関数内で doshowsrc1 の場合、showsrc 関数が呼び出され、ソースコードの表示が行われます。
  2. -n x フラグ:
    • 目的: x 行未満の短い未実行コードセクションに関する報告を抑制します。これにより、ノイズの多い出力を減らし、より重要な未実行セクションに焦点を当てることができます。
    • 実装: minlines という新しいグローバル変数が導入され、-n フラグの引数 x がこの変数に整数として格納されます。missing 関数内で、未実行セクションの行数が minlines よりも小さい場合、そのセクションに関する出力はスキップされます。

showsrc 関数の追加

このコミットの主要な変更点の一つは、showsrc という新しい関数の追加です。この関数は、指定されたファイルと行範囲のソースコードを読み込み、標準出力に整形して表示する役割を担います。

  • 引数: showsrc(char *file, int line1, int line2)
    • file: ソースファイルへのパス。
    • line1: 表示を開始する行番号。
    • line2: 表示を終了する行番号。
  • 動作:
    1. 指定されたファイルを Bopen で開きます。
    2. line1 まで行を読み飛ばします。
    3. line1 から最大5行(または line2 まで)のソースコードを読み込み、行番号とともに print 関数で出力します。
    4. もし line2 に到達する前に5行の表示制限に達した場合、残りの行があることを示すために ... を出力します。
    5. ファイルを閉じます。

missing 関数の変更

missing 関数は、テストで実行されなかったコードセクションを特定し、報告する役割を担っています。このコミットでは、missing 関数が以下のように変更されました。

  • ファイル名と行番号の取得ロジックの改善: 以前は src1src2 という2つのバッファを使ってファイル名と行番号を処理していましたが、より簡潔に fileline1, line2 を直接取得するように変更されました。特に、fileline 関数から返される文字列からファイル名と行番号をパースするロジックが追加されました。
  • minlines によるフィルタリング: 未実行セクションの行数 (line2+1-line1) が minlines よりも小さい場合、return して出力を抑制します。
  • 出力フォーマットの変更:
    • 関数全体が呼び出されなかった場合 (pc == s.value) の出力フォーマットが、shortname(src1) から shortname(file)line1 を含む形式に変更されました。
    • 未実行セクションの出力フォーマットも、line1line2 を含み、より詳細な情報を提供するように変更されました。
  • showsrc の呼び出し: doshowsrc が有効な場合、showsrc 関数を呼び出して関連するソースコードを表示します。

コアとなるコードの変更箇所

src/cmd/cov/main.c ファイルにおける主要な変更点は以下の通りです。

  1. usage 関数の変更:

    --- a/src/cmd/cov/main.c
    +++ b/src/cmd/cov/main.c
    @@ -21,7 +21,7 @@ typedef struct Ureg Ureg;
     void
     usage(void)
     {
    -	fprint(2, "usage: cov [-lv] [-g regexp] [6.out args...]\\n");
    +	fprint(2, "usage: cov [-lsv] [-g regexp] [-m minlines] [6.out args...]\\n");
     	fprint(2, "-g specifies pattern of interesting functions or files\\n");
     	exits("usage");
     }
    

    新しいフラグ -s-m minlines (コミットメッセージでは -n x と記載されているが、コードでは -m を使用) が usage メッセージに追加されました。

  2. 新しいグローバル変数の追加:

    --- a/src/cmd/cov/main.c
    +++ b/src/cmd/cov/main.c
    @@ -37,12 +37,14 @@ int chatty;
     int fd;
     int longnames;
     int pid;
    +int doshowsrc;
     Map *mem;
     Map *text;
     Fhdr fhdr;
     Reprog *grep;
     char cwd[1000];
     int ncwd;
    +int minlines = -1000;
    

    doshowsrc (ソース表示フラグ) と minlines (最小行数フィルタ) が追加されました。minlines の初期値 -1000 は、デフォルトではフィルタリングが無効であることを意味します。

  3. showsrc 関数の追加:

    +void
    +showsrc(char *file, int line1, int line2)
    +{
    +	Biobuf *b;
    +	char *p;
    +	int n, stop;
    +
    +	if((b = Bopen(file, OREAD)) == nil) {
    +		print("\topen %s: %r\n", file);
    +		return;
    +	}
    +
    +	for(n=1; n<line1 && (p = Brdstr(b, '\n', 1)) != nil; n++)
    +		free(p);
    +
    +	// print up to five lines (this one and 4 more).
    +	// if there are more than five lines, print 4 and "..."
    +	stop = n+4;
    +	if(stop > line2)
    +		stop = line2;
    +	if(stop < line2)
    +		stop--;
    +	for(; n<=stop && (p = Brdstr(b, '\n', 1)) != nil; n++) {
    +		print("  %d %s\n", n, p);
    +		free(p);
    +	}
    +	if(n < line2)
    +		print("  ...\n");
    +	Bterm(b);
    +}
    

    ソースコードを表示するための新しいヘルパー関数が追加されました。

  4. missing 関数の変更:

    --- a/src/cmd/cov/main.c
    +++ b/src/cmd/cov/main.c
    @@ -125,20 +158,36 @@ shortname(char *s)
     void
     missing(uvlong pc, uvlong epc)
     {
    -	char src1[1000];
    -	char src2[1000];
    +	char file[1000];
    +	int line1, line2;
     	char buf[100];
     	Symbol s;
     	char *p;
    +	uvlong uv;
     
    -	if(!findsym(pc, CTEXT, &s) || !fileline(src1, sizeof src1, pc) || !fileline(src2, sizeof src2, pc)) {
    +	if(!findsym(pc, CTEXT, &s) || !fileline(file, sizeof file, pc)) {
    +	notfound:
     		print("%#llux-%#llux\\n", pc, epc);
     		return;
     	}
    +	p = strrchr(file, ':');
    +	*p++ = 0;
    +	line1 = atoi(p);
    +	for(uv=pc; uv<epc; ) {
    +		if(!fileline(file, sizeof file, epc-2))
    +			goto notfound;
    +		uv += machdata->instsize(text, uv);
    +	}
    +	p = strrchr(file, ':');
    +	*p++ = 0;
    +	line2 = atoi(p);
    +
    +	if(line2+1-line2 < minlines)
    +		return;
     
      if(pc == s.value) {
      	// never entered function
    -	print("%s %s never called (%#llux-%#llux)\\n", shortname(src1), s.name, pc, epc);
    +	print("%s:%d %s never called (%#llux-%#llux)\\n", shortname(file), line1, s.name, pc, epc);
      	return;
      }
      if(pc <= s.value+13) {
    @@ -204,13 +253,14 @@ missing(uvlong pc, uvlong epc)
      // show first instruction to make clear where we were.
      machdata->das(text, pc, 0, buf, sizeof buf);
     
    -	// cut filename off src2, leaving just line number.
    -	p = strrchr(src2, ':');
    -	if(p != nil)
    -		p++;
    +	if(line1 != line2)
    +		print("%s:%d,%d %#llux-%#llux %s\\n",
    +			shortname(file), line1, line2, pc, epc, buf);
      else
    -		p = src2;
    -	print("%s,%s %s %#llux-%#llux %s\\n", shortname(src1), p, s.name, pc, epc, buf);
    +		print("%s:%d %#llux-%#llux %s\\n",
    +			shortname(file), line1, pc, epc, buf);
    +	if(doshowsrc)
    +		showsrc(file, line1, line2);
     }
    

    ファイル名と行番号の取得ロジックが変更され、minlines によるフィルタリングと showsrc の呼び出しが追加されました。

  5. main 関数の引数解析の変更:

    --- a/src/cmd/cov/main.c
    +++ b/src/cmd/cov/main.c
    @@ -387,6 +437,12 @@ main(int argc, char **argv)
     	case 'l':
     		longnames++;
     		break;
    +	case 'n':
    +		minlines = atoi(EARGF(usage()));
    +		break;
    +	case 's':
    +		doshowsrc = 1;
    +		break;
     	case 'v':
     		chatty++;
     		break;
    

    main 関数内の ARGBEGIN ブロックに、新しいフラグ -n-s の処理が追加されました。-n は引数を整数として minlines に設定し、-sdoshowsrc1 に設定します。

コアとなるコードの解説

このコミットの核心は、6cov の出力の粒度と詳細度を制御する機能を追加した点にあります。

showsrc 関数

showsrc 関数は、未実行コードセクションのコンテキストを開発者に提供するために導入されました。この関数は、指定されたソースファイルから特定の行範囲を読み込み、整形して出力します。

  • ファイルI/O: Biobuf を使用してファイルを効率的に読み込みます。これは、Go言語の初期のC言語ベースのツールにおける一般的なファイル操作パターンです。
  • 行のスキップと読み込み: Brdstr を使用して行単位で文字列を読み込み、line1 までスキップした後、指定された行範囲(または最大5行)を読み込みます。
  • 出力フォーマット: 各行には行番号が付加され、整形された形で出力されます。また、表示制限により全ての行が表示できない場合は ... を出力することで、さらに多くのコードが存在することを示します。

この関数により、開発者は 6cov のレポートから直接、未実行のコードがどのようなものかを視覚的に確認できるようになり、デバッグやテストケースの作成が容易になります。

minlinesdoshowsrc の連携

minlinesdoshowsrc のグローバル変数は、missing 関数内で連携して動作します。

  • minlines は、未実行セクションの行数がこの値よりも小さい場合に、そのセクションの報告を抑制するための閾値として機能します。これにより、例えばコメント行のみのブロックや非常に短いコードスニペットなど、重要度の低い未実行セクションがレポートから除外され、開発者はより意味のある情報に集中できます。
  • doshowsrc は、-s フラグが指定された場合に 1 に設定され、missing 関数内で showsrc 関数を呼び出すかどうかを制御します。これにより、開発者は必要に応じてソースコードの表示を有効/無効に切り替えることができます。

これらの機能は、6cov の出力をより柔軟にし、開発者のニーズに合わせてカスタマイズできるようにすることで、ツールの実用性を大幅に向上させました。

関連リンク

現在のGo言語におけるコードカバレッジツールに関する情報:

参考にした情報源リンク