[インデックス 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/atomic
package documentation: https://pkg.go.dev/sync/atomic