[インデックス 18890] ファイルの概要
このコミットは、Go言語のコマンドラインツール go の動作に関する変更です。具体的には、go test コマンドでコードカバレッジを計測する際のデフォルトのモードが、データ競合検出機能である -race フラグが有効になっている場合に「atomic」モードになるように修正されています。
コミット
commit 108a4dcd75713a1a039db5bb5e21a8d840e50472
Author: Rob Pike <r@golang.org>
Date: Tue Mar 18 14:38:40 2014 +1100
cmd/go: make the default coverage mode -atomic if -race is set
Fixes #7013.
LGTM=adg
R=golang-codereviews, gobot, adg
CC=golang-codereviews
https://golang.org/cl/76370043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/108a4dcd75713a1a039db5bb5e21a8d840e50472
元コミット内容
cmd/go: make the default coverage mode -atomic if -race is set
(cmd/go: -race が設定されている場合、デフォルトのカバレッジモードを -atomic にする)
Fixes #7013.
(Issue #7013 を修正)
変更の背景
この変更の背景には、Go言語のテストツールにおけるコードカバレッジ計測とデータ競合検出の連携に関する課題がありました。
Go言語の go test -cover コマンドは、テスト実行時にコードカバレッジを計測する機能を提供します。このカバレッジ計測にはいくつかのモードがあり、それぞれ計測方法が異なります。
一方、go test -race コマンドは、プログラム実行中に発生する可能性のあるデータ競合(複数のゴルーチンが同時に共有データにアクセスし、少なくとも一方が書き込みを行う場合に発生する競合状態)を検出するためのツールです。データ競合は並行処理における深刻なバグの原因となり、特定が非常に困難です。-race フラグを有効にすると、Goランタイムが追加のインストゥルメンテーション(計測コードの埋め込み)を行い、競合を検出します。
問題は、デフォルトのカバレッジモード (set) が、-race フラグと組み合わせて使用された場合に、データ競合検出の正確性や信頼性に影響を与える可能性があったことです。特に、カバレッジ計測のために挿入されるコードが、競合検出器の動作に予期せぬ影響を与えたり、あるいは競合検出器がカバレッジ計測コードの実行を妨げたりする可能性が考えられます。
atomic カバレッジモードは、並行実行環境でのカバレッジ計測に適しており、アトミック操作(不可分操作)を使用して計測を行うため、データ競合の影響を受けにくい特性があります。したがって、-race フラグが有効な場合にデフォルトのカバレッジモードを atomic にすることで、データ競合検出とコードカバレッジ計測の両方がより正確かつ信頼性高く機能するようになります。
この変更は、Go言語のIssue #7013 (cmd/go: -race and -cover should imply -covermode=atomic) に対応するものです。
前提知識の解説
Go言語の go test コマンド
go test は、Go言語のパッケージのテストを実行するためのコマンドです。様々なフラグをサポートしており、テストの挙動を制御できます。
コードカバレッジ (-cover フラグ)
go test -cover は、テストがソースコードのどの部分を実行したかを計測し、その割合(カバレッジ)を報告します。これにより、テストの網羅性を評価できます。
コードカバレッジモード (-covermode フラグ)
-covermode フラグは、カバレッジ計測のモードを指定します。主なモードは以下の通りです。
set(デフォルト): 各ステートメントが実行されたかどうか(真偽値)のみを記録します。最も軽量なモードです。count: 各ステートメントが何回実行されたかを記録します。より詳細な情報を提供しますが、オーバーヘッドが増加します。atomic:countモードと同様に実行回数を記録しますが、並行実行環境でのデータ競合を避けるためにアトミック操作を使用します。これにより、複数のゴルーチンが同時にカバレッジカウンタを更新しても、正確なカウントが保証されます。オーバーヘッドは最も大きくなります。
データ競合検出 (-race フラグ)
go test -race は、Goプログラムの実行中にデータ競合を検出するためのツールです。Goランタイムに組み込まれた競合検出器が、メモリへのアクセスを監視し、複数のゴルーチンが同時に共有メモリにアクセスし、少なくとも一方が書き込みを行う場合に警告を発します。これは、並行プログラミングにおける最も一般的なバグの一つであり、デバッグが非常に困難なため、このツールは非常に重要です。-race フラグを有効にすると、プログラムの実行速度は低下し、メモリ使用量も増加します。
アトミック操作
アトミック操作とは、複数のスレッド(またはゴルーチン)から同時にアクセスされた場合でも、その操作全体が不可分(中断されない)であることを保証する操作です。これにより、並行処理環境でのデータの一貫性が保たれます。Go言語では sync/atomic パッケージでアトミック操作が提供されています。カバレッジ計測において atomic モードが使用されるのは、複数のゴルーチンが同時にカバレッジカウンタを更新する可能性があるため、正確な計測を保証するためです。
技術的詳細
このコミットは、go test コマンドの内部ロジックを変更し、-race フラグが指定された場合に -covermode のデフォルト値を atomic に設定するようにします。
具体的には、以下のファイルが変更されています。
src/cmd/go/doc.go:go testコマンドのヘルプドキュメントを更新し、-covermodeのデフォルト値が-raceフラグの有無によって変わることを明記しています。- 変更前:
The default is "set". - 変更後:
The default is "set" unless -race is enabled, in which case it is "atomic".
- 変更前:
src/cmd/go/test.go:go testコマンドのヘルプドキュメントを更新し、doc.goと同様の変更を加えています。- 変更前:
The default is "set". - 変更後:
The default is "set" unless -race is enabled, in which case it is "atomic".
- 変更前:
src/cmd/go/testflag.go: ここが主要なロジックの変更箇所です。testFlags関数内で、testCoverModeのデフォルト値を決定するロジックが追加されています。- 以前は
testCoverMode = "set"と直接初期化されていましたが、この行が削除されました。 - 代わりに、
testCoverModeが空文字列の場合(つまりユーザーが明示的に-covermodeを指定しなかった場合)に、buildRace(これは-raceフラグが有効かどうかを示す内部変数) の値に基づいてtestCoverModeを設定するロジックが追加されました。buildRaceがtrueの場合、testCoverModeは"atomic"に設定されます。- それ以外の場合、
testCoverModeは"set"に設定されます。
- 以前は
src/cmd/go/test.bash:go testコマンドのテストスクリプトが更新され、この新しいデフォルト挙動を検証するテストケースが追加されています。-coverpkgと-coverprofileを使用してカバレッジを計測し、生成されたプロファイルファイル (testdata/cover.out) の内容をgrepで確認することで、期待されるカバレッジモードが適用されているかを検証しています。- 具体的には、
-raceなしでsetモードが適用されること。-raceありでatomicモードが適用されること。-raceありでも、明示的に-covermode=countを指定した場合はcountモードが優先されること。 これらのテストケースが追加され、変更が正しく機能することを確認しています。
この変更により、ユーザーが go test -race -cover のようにコマンドを実行した場合、明示的に -covermode を指定しなくても、自動的に atomic モードでカバレッジが計測されるようになります。これにより、データ競合検出とカバレッジ計測の間の潜在的な問題を回避し、より堅牢なテスト環境を提供します。
コアとなるコードの変更箇所
src/cmd/go/testflag.go
--- a/src/cmd/go/testflag.go
+++ b/src/cmd/go/testflag.go
@@ -117,7 +117,6 @@ var testFlagDefn = []*testFlagSpec{\
func testFlags(args []string) (packageNames, passToTest []string) {
inPkg := false
outputDir := ""
- testCoverMode = "set"
for i := 0; i < len(args); i++ {
if !strings.HasPrefix(args[i], "-") {
if !inPkg && packageNames == nil {
@@ -218,6 +217,14 @@ func testFlags(args []string) (packageNames, passToTest []string) {
}
}
+ if testCoverMode == "" {
+ testCoverMode = "set"
+ if buildRace {
+ // Default coverage mode is atomic when -race is set.
+ testCoverMode = "atomic"
+ }
+ }
+
// Tell the test what directory we're running in, so it can write the profiles there.
if testProfile && outputDir == "" {
dir, err := os.Getwd()
コアとなるコードの解説
src/cmd/go/testflag.go の testFlags 関数は、go test コマンドに渡された引数を解析し、テストの実行に必要なフラグやパッケージ名を抽出する役割を担っています。
変更前は、testCoverMode = "set" という行で、カバレッジモードのデフォルト値が常に "set" に初期化されていました。
変更後、この初期化行が削除され、代わりに以下のロジックが追加されました。
if testCoverMode == "" {
testCoverMode = "set"
if buildRace {
// Default coverage mode is atomic when -race is set.
testCoverMode = "atomic"
}
}
このコードブロックは、以下の挙動を実現します。
if testCoverMode == "":ユーザーがコマンドラインで明示的に-covermodeフラグを指定しなかった場合(testCoverModeが初期値の空文字列のままの場合)に、このブロック内の処理が実行されます。testCoverMode = "set":まず、デフォルトのカバレッジモードを"set"に設定します。これは、-raceフラグが有効でない場合の通常のデフォルト挙動です。if buildRace:次に、buildRace変数の値を確認します。buildRaceは、go testコマンドに-raceフラグが指定された場合にtrueに設定される内部変数です。testCoverMode = "atomic":もしbuildRaceがtrueであれば、カバレッジモードを"atomic"に上書きします。これにより、-raceと-coverが同時に使用された場合に、自動的にatomicモードが選択されるようになります。コメント// Default coverage mode is atomic when -race is set.がこの意図を明確に示しています。
この変更により、go test コマンドは、ユーザーの意図(データ競合検出を有効にする)をより適切に解釈し、それに最適なカバレッジ計測モードを自動的に選択するようになりました。これにより、ユーザーは -race と -cover を同時に使用する際に、追加で -covermode=atomic を指定する手間が省け、かつより信頼性の高いカバレッジ結果と競合検出結果を得られるようになります。
関連リンク
- Go Issue #7013: cmd/go: -race and -cover should imply -covermode=atomic
- Go Code Review: https://golang.org/cl/76370043
参考にした情報源リンク
- Go Command Documentation:
go help test(特に-coverと-raceフラグに関する説明) - Go Blog: Introducing the Go Race Detector
- Go Blog: Go 1.2 Coverage
- Go
sync/atomicpackage documentation: https://pkg.go.dev/sync/atomic