[インデックス 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の別解釈として)