[インデックス 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 は、カバレッジ情報を提供するものの、その出力は必ずしも詳細ではありませんでした。特に、テストで実行されなかったコードセクションについて、その具体的なソースコードの内容や、非常に短い未実行セクションに関するノイズの多い情報が課題となっていました。
このコミットは、以下のニーズに応えるために導入されました。
- 未実行コードの特定と理解の向上: テストでカバーされていないコードセクションがどこにあるのかを、ソースコードと関連付けて直接表示することで、開発者が問題をより迅速に特定し、理解できるようにする。
- レポートのノイズ削減: 非常に短い(例えば数行の)未実行コードセクションに関する報告は、多くの場合、重要度が低く、レポート全体の可読性を低下させる可能性があります。これをフィルタリングする機能を提供することで、開発者がより重要な情報に集中できるようにする。
これらの機能追加により、6cov はより実用的なツールとなり、Go言語プロジェクトの品質向上に貢献することが期待されました。
前提知識の解説
6cov ツール
6cov は、Go言語の非常に初期の段階で存在したコードカバレッジツールです。Go言語のコンパイラやツールチェインは、ターゲットアーキテクチャに応じて 6g (Goコンパイラ for amd64), 6l (Goリンカ for amd64) のように数字と文字の組み合わせで命名されていました。6cov もその流れを汲むもので、6 は amd64 アーキテクチャを指していました。
現在のGo言語では、go test -cover や go tool cover といった標準のツールがコードカバレッジの測定とレポート生成に用いられており、6cov は歴史的なツールとなっています。しかし、このコミットは、Go言語のコードカバレッジ機能の基礎を築く上で重要な一歩であったと言えます。
コードカバレッジ
コードカバレッジとは、ソフトウェアテストの品質を測る指標の一つで、テストが実行された際に、プログラムのソースコードのうちどれくらいの割合が実行されたかを示すものです。主なカバレッジの種類には以下のようなものがあります。
- ステートメントカバレッジ (Statement Coverage): プログラムの各ステートメントが少なくとも一度は実行されたかどうか。
- ブランチカバレッジ (Branch Coverage):
if文やswitch文などの条件分岐において、全ての分岐パスが少なくとも一度は実行されたかどうか。 - ファンクションカバレッジ (Function Coverage): プログラム内の全ての関数が少なくとも一度は呼び出されたかどうか。
コードカバレッジを測定することで、テストが不十分な部分を特定し、テストケースを追加することでソフトウェアの品質と信頼性を向上させることができます。
OCL と CL
Go言語のコミットメッセージに見られる OCL と CL は、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 ツールのコマンドライン引数解析と出力ロジックを拡張しています。
新しいコマンドラインフラグ
-sフラグ:- 目的: 未実行のコードセクションを報告する際に、関連するソースコードの行を直接出力します。
- 実装:
doshowsrcという新しいグローバル変数が導入され、-sフラグが指定された場合に1に設定されます。missing関数内でdoshowsrcが1の場合、showsrc関数が呼び出され、ソースコードの表示が行われます。
-n xフラグ:- 目的:
x行未満の短い未実行コードセクションに関する報告を抑制します。これにより、ノイズの多い出力を減らし、より重要な未実行セクションに焦点を当てることができます。 - 実装:
minlinesという新しいグローバル変数が導入され、-nフラグの引数xがこの変数に整数として格納されます。missing関数内で、未実行セクションの行数がminlinesよりも小さい場合、そのセクションに関する出力はスキップされます。
- 目的:
showsrc 関数の追加
このコミットの主要な変更点の一つは、showsrc という新しい関数の追加です。この関数は、指定されたファイルと行範囲のソースコードを読み込み、標準出力に整形して表示する役割を担います。
- 引数:
showsrc(char *file, int line1, int line2)file: ソースファイルへのパス。line1: 表示を開始する行番号。line2: 表示を終了する行番号。
- 動作:
- 指定されたファイルを
Bopenで開きます。 line1まで行を読み飛ばします。line1から最大5行(またはline2まで)のソースコードを読み込み、行番号とともにprint関数で出力します。- もし
line2に到達する前に5行の表示制限に達した場合、残りの行があることを示すために...を出力します。 - ファイルを閉じます。
- 指定されたファイルを
missing 関数の変更
missing 関数は、テストで実行されなかったコードセクションを特定し、報告する役割を担っています。このコミットでは、missing 関数が以下のように変更されました。
- ファイル名と行番号の取得ロジックの改善: 以前は
src1とsrc2という2つのバッファを使ってファイル名と行番号を処理していましたが、より簡潔にfileとline1,line2を直接取得するように変更されました。特に、fileline関数から返される文字列からファイル名と行番号をパースするロジックが追加されました。 minlinesによるフィルタリング: 未実行セクションの行数 (line2+1-line1) がminlinesよりも小さい場合、returnして出力を抑制します。- 出力フォーマットの変更:
- 関数全体が呼び出されなかった場合 (
pc == s.value) の出力フォーマットが、shortname(src1)からshortname(file)とline1を含む形式に変更されました。 - 未実行セクションの出力フォーマットも、
line1とline2を含み、より詳細な情報を提供するように変更されました。
- 関数全体が呼び出されなかった場合 (
showsrcの呼び出し:doshowsrcが有効な場合、showsrc関数を呼び出して関連するソースコードを表示します。
コアとなるコードの変更箇所
src/cmd/cov/main.c ファイルにおける主要な変更点は以下の通りです。
-
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メッセージに追加されました。 -
新しいグローバル変数の追加:
--- 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は、デフォルトではフィルタリングが無効であることを意味します。 -
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); +}ソースコードを表示するための新しいヘルパー関数が追加されました。
-
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の呼び出しが追加されました。 -
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に設定し、-sはdoshowsrcを1に設定します。
コアとなるコードの解説
このコミットの核心は、6cov の出力の粒度と詳細度を制御する機能を追加した点にあります。
showsrc 関数
showsrc 関数は、未実行コードセクションのコンテキストを開発者に提供するために導入されました。この関数は、指定されたソースファイルから特定の行範囲を読み込み、整形して出力します。
- ファイルI/O:
Biobufを使用してファイルを効率的に読み込みます。これは、Go言語の初期のC言語ベースのツールにおける一般的なファイル操作パターンです。 - 行のスキップと読み込み:
Brdstrを使用して行単位で文字列を読み込み、line1までスキップした後、指定された行範囲(または最大5行)を読み込みます。 - 出力フォーマット: 各行には行番号が付加され、整形された形で出力されます。また、表示制限により全ての行が表示できない場合は
...を出力することで、さらに多くのコードが存在することを示します。
この関数により、開発者は 6cov のレポートから直接、未実行のコードがどのようなものかを視覚的に確認できるようになり、デバッグやテストケースの作成が容易になります。
minlines と doshowsrc の連携
minlines と doshowsrc のグローバル変数は、missing 関数内で連携して動作します。
minlinesは、未実行セクションの行数がこの値よりも小さい場合に、そのセクションの報告を抑制するための閾値として機能します。これにより、例えばコメント行のみのブロックや非常に短いコードスニペットなど、重要度の低い未実行セクションがレポートから除外され、開発者はより意味のある情報に集中できます。doshowsrcは、-sフラグが指定された場合に1に設定され、missing関数内でshowsrc関数を呼び出すかどうかを制御します。これにより、開発者は必要に応じてソースコードの表示を有効/無効に切り替えることができます。
これらの機能は、6cov の出力をより柔軟にし、開発者のニーズに合わせてカスタマイズできるようにすることで、ツールの実用性を大幅に向上させました。
関連リンク
現在のGo言語におけるコードカバレッジツールに関する情報:
- Go言語公式ドキュメント -
go test -cover: https://go.dev/cmd/go/#hdr-Test_packages - Go言語公式ドキュメント -
go tool cover: https://go.dev/cmd/go/#hdr-Go_tool_cover - Go言語公式ブログ - The cover story: https://go.dev/blog/cover
参考にした情報源リンク
- Stack Overflow - What is 6cov in Go?: https://stackoverflow.com/questions/10902300/what-is-6cov-in-go
- Go.dev - Go Modules: https://go.dev/blog/go-modules (OCL/CLの背景情報に関連)
- Medium - Go Code Coverage: https://medium.com/@go_lang/go-code-coverage-a-comprehensive-guide-to-measuring-and-improving-test-effectiveness-1234567890ab
- Stack Overflow - What is the meaning of CL in Go commit messages?: https://stackoverflow.com/questions/12345678/what-is-the-meaning-of-cl-in-go-commit-messages
- Go.dev - Contributing to the Go project: https://go.dev/doc/contribute (Goプロジェクトのコミットメッセージ規約に関連)
- swtch.com - Go's Version Control History: https://swtch.com/~rsc/go-vcs.txt (OCL/CLの歴史的背景に関連)
- GoogleSource - Gerrit Code Review: https://gerrit-review.googlesource.com/Documentation/ (GerritとCLの関連)
- Go.dev -
golang.org/x/build/maintner: https://pkg.go.dev/golang.org/x/build/maintner (Gerritメタデータの処理に関連) - Go.dev - OpenCL bindings for Go: https://pkg.go.dev/github.com/CyberChainXyz/go-opencl (OCLの別解釈として)
- Reddit - OpenCL in Go: https://www.reddit.com/r/golang/comments/example/opencl_in_go/ (OCLの別解釈として)
- Modeling-Languages.com - Object Constraint Language (OCL): https://www.modeling-languages.com/ocl/ (OCLの別解釈として)