[インデックス 17300] ファイルの概要
このコミットは、Goランタイムのデータ競合検出器(Race Detector)の出力テストに関するものです。具体的には、src/pkg/runtime/race/output_test.go
ファイルが変更されており、GORACE
環境変数の様々なパラメータ(atexit_sleep_ms
, exitcode
, strip_path_prefix
, halt_on_error
)がRace Detectorの出力にどのように影響するかを検証するためのテストケースが追加されています。
コミット
commit 18f5ce856191062224d4bb08c7c2623296aeeaa2
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Fri Aug 16 21:54:04 2013 +0400
runtime/race: add output tests for different GORACE params
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13065043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/18f5ce856191062224d4bb08c7c2623296aeeaa2
元コミット内容
runtime/race: add output tests for different GORACE params
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13065043
変更の背景
GoのRace Detectorは、並行処理におけるデータ競合を検出するための強力なツールです。その動作はGORACE
環境変数によって細かく制御できます。しかし、これらのパラメータが実際にどのように出力やプログラムの挙動に影響を与えるかを検証するテストが不足していました。このコミットは、GORACE
の様々な設定が期待通りに機能することを確認し、将来的な変更に対する回帰テストを提供することを目的としています。特に、Race Detectorの出力形式や終了コード、パスの表示方法、エラー発生時の挙動などを網羅的にテストすることで、ツールの信頼性と堅牢性を向上させます。
前提知識の解説
Go Race Detector (データ競合検出器)
Go Race Detectorは、Goプログラムにおけるデータ競合(data race)を検出するためのツールです。データ競合とは、複数のゴルーチンが同時に同じメモリ位置にアクセスし、少なくとも1つのアクセスが書き込みであり、かつそれらのアクセスが同期メカニズムによって保護されていない場合に発生するバグです。データ競合はプログラムの予測不能な動作やクラッシュを引き起こす可能性があり、デバッグが非常に困難です。Go Race Detectorは、プログラムの実行時にこれらの競合を検出し、詳細なレポート(競合が発生した場所、関連するゴルーチンスタックトレースなど)を出力します。
GORACE 環境変数
GORACE
は、Go Race Detectorの動作を制御するための環境変数です。この変数は、option=value
の形式で複数の設定をカンマ区切りで指定できます。このコミットでテストされている主要なパラメータは以下の通りです。
atexit_sleep_ms
:- 目的: Race Detectorがデータ競合を検出した後、プログラムが終了する前にどれくらいの時間(ミリ秒単位)スリープするかを制御します。
- 背景: C言語の
atexit()
関数や、メインプログラムロジックが完了した後にバックグラウンドスレッドで実行される可能性のあるクリーンアップルーチンなど、終了時に発生する競合を捕捉するのに役立ちます。 - デフォルト: 1000ミリ秒(1秒)。
atexit_sleep_ms=0
: スリープを無効にします。
exitcode
:- 目的: データ競合が検出された場合に、プログラムが終了する際の終了ステータスコードを指定します。
- デフォルト: 66。
- 用途: CI/CDパイプラインなどで、Race Detectorが競合を検出した際に特定の終了コードを返すように設定することで、ビルドやテストの失敗を自動的に検知できます。
strip_path_prefix
:- 目的: Race Detectorのレポートに出力されるファイルパスから、指定されたプレフィックスを削除します。
- 用途: 長いパスや深くネストされたディレクトリ構造を持つプロジェクトにおいて、レポートをより簡潔で読みやすくするために使用されます。例えば、
/home/user/project/src/main.go
のようなパスから/home/user/project/src/
を削除してmain.go
とだけ表示させることができます。
halt_on_error
:- 目的: データ競合が検出された際に、プログラムの実行を直ちに停止するかどうかを制御します。
- デフォルト: Race Detectorは競合を検出しても、そのトレースを出力し、プログラムの実行を継続します。
halt_on_error=1
: 最初のデータ競合が検出された時点でプログラムを即座に終了させます。- 用途: テスト環境で「フェイルファスト(fail-fast)」な挙動を実現し、競合状態を迅速に特定して対処するのに役立ちます。
技術的詳細
このコミットでは、src/pkg/runtime/race/output_test.go
ファイル内のTestOutput
関数とtests
スライスが拡張されています。
TestOutput
関数は、Goのサブプロセスとしてテスト対象のプログラムを実行し、その標準出力と標準エラー出力をキャプチャして、期待される正規表現パターンと照合します。
変更点としては、TestOutput
関数内でサブプロセスを実行する際に、os.Environ()
から既存の環境変数をコピーするループに、GORACE=
で始まる環境変数をスキップする条件が追加されました。これは、テストケースごとに異なるGORACE
設定を適用するために、既存のGORACE
設定がテストの邪魔にならないようにするためです。その後、各テストケースで定義されたtest.gorace
の値がcmd.Env
に明示的に追加され、サブプロセスに渡されます。
tests
スライスには、新しいテストケースが追加されています。各テストケースは以下のフィールドを持ちます。
name
: テストケースの名前。gorace
: このテストケースで設定するGORACE
環境変数の値。source
: テスト対象となるGoプログラムのソースコード。このコードはデータ競合を引き起こすように設計されています。re
: テスト対象プログラムの出力がマッチすべき正規表現パターン。
これらのテストケースは、それぞれ異なるGORACE
パラメータを設定した上でデータ競合を発生させ、その出力が期待通りの形式や内容になっているかを検証します。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/race/output_test.go
+++ b/src/pkg/runtime/race/output_test.go
@@ -41,11 +41,13 @@ func TestOutput(t *testing.T) {
// GODEBUG spoils program output, GOMAXPROCS makes it flaky.
for _, env := range os.Environ() {
if strings.HasPrefix(env, "GODEBUG=") ||
- strings.HasPrefix(env, "GOMAXPROCS=") {
+ strings.HasPrefix(env, "GOMAXPROCS=") ||
+ strings.HasPrefix(env, "GORACE=") {
continue
}
cmd.Env = append(cmd.Env, env)
}
+ cmd.Env = append(cmd.Env, "GORACE="+test.gorace)
got, _ := cmd.CombinedOutput()
if !regexp.MustCompile(test.re).MatchString(string(got)) {
t.Fatalf("failed test case %v, expect:\\n%v\\ngot:\\n%s",
@@ -56,10 +58,11 @@ func TestOutput(t *testing.T) {
var tests = []struct {
name string
+\tgorace string
source string
re string
}{
-\t{\"simple\", `\
+\t{\"simple\", \"atexit_sleep_ms=0\", `\
package main
import "time"
func main() {
@@ -84,23 +87,70 @@ func racer(x *int, done chan bool) {
WARNING: DATA RACE
Write by goroutine [0-9]:
main\.store\(\)
- .*/main\.go:12 \+0x[0-9,a-f]+\n+ .+/main\.go:12 \+0x[0-9,a-f]+\n
main\.racer\(\)
- .*/main\.go:19 \+0x[0-9,a-f]+\n+ .+/main\.go:19 \+0x[0-9,a-f]+\n
Previous write by main goroutine:
main\.store\(\)
- .*/main\.go:12 \+0x[0-9,a-f]+\n+ .+/main\.go:12 \+0x[0-9,a-f]+\n
main\.main\(\)
- .*/main\.go:8 \+0x[0-9,a-f]+\n+ .+/main\.go:8 \+0x[0-9,a-f]+\n
Goroutine [0-9] \(running\) created at:
main\.startRacer\(\)
- .*/main\.go:15 \+0x[0-9,a-f]+\n+ .+/main\.go:15 \+0x[0-9,a-f]+\n
main\.main\(\)
- .*/main\.go:7 \+0x[0-9,a-f]+\n+ .+/main\.go:7 \+0x[0-9,a-f]+\n
==================
Found 1 data race\(s\)
exit status 66
+`},\n+\n+\t{\"exitcode\", \"atexit_sleep_ms=0 exitcode=13\", `\
+package main
+func main() {\n+\tdone := make(chan bool)\n+\tx := 0\n+\tgo func() {\n+\t\tx = 42\n+\t\tdone <- true\n+\t}()\n+\tx = 43\n+\t<-done\n+}\n+`, `exit status 13`},\n+\n+\t{\"strip_path_prefix\", \"atexit_sleep_ms=0 strip_path_prefix=/main.\", `\
+package main
+func main() {\n+\tdone := make(chan bool)\n+\tx = 0\n+\tgo func() {\n+\t\tx = 42\n+\t\tdone <- true\n+\t}()\n+\tx = 43\n+\t<-done\n+}\n+`, `\n+ go:7 \\+0x[0-9,a-f]+\n+`},\n+\n+\t{\"halt_on_error\", \"atexit_sleep_ms=0 halt_on_error=1\", `\
+package main
+func main() {\n+\tdone := make(chan bool)\n+\tx = 0\n+\tgo func() {\n+\t\tx = 42\n+\t\tdone <- true\n+\t}()\n+\tx = 43\n+\t<-done\n+}\n+`, `\n+==================\n+exit status 66\n `},\n }\n```
## コアとなるコードの解説
このコミットのコアとなる変更は、`output_test.go`ファイルにおける`TestOutput`関数の環境変数設定の修正と、`tests`スライスへの新しいテストケースの追加です。
1. **環境変数設定の修正**:
`TestOutput`関数内で、サブプロセスに渡す環境変数を構築する際に、既存の`GORACE=`で始まる環境変数をスキップするようになりました。これにより、各テストケースで明示的に設定される`GORACE`変数が、以前のテスト実行やシステム環境の影響を受けずに適用されることが保証されます。その後、`cmd.Env = append(cmd.Env, "GORACE="+test.gorace)`によって、現在のテストケースに特化した`GORACE`設定が追加されます。
2. **新しいテストケースの追加**:
`tests`スライスに以下の4つの新しいテストケースが追加されました。
* **`"simple"` (変更)**:
* `gorace`: `"atexit_sleep_ms=0"`
* **解説**: 既存の`"simple"`テストケースに`atexit_sleep_ms=0`が追加されました。これは、Race Detectorが競合を検出した後に終了する際のスリープを無効にし、テストの実行時間を短縮することを目的としています。正規表現パターンも、パスのプレフィックスがより汎用的な`.*`から`.+`に変更され、より柔軟なパスマッチングに対応しています。
* **`"exitcode"`**:
* `gorace`: `"atexit_sleep_ms=0 exitcode=13"`
* **解説**: このテストケースは、`exitcode`パラメータの動作を検証します。データ競合が発生するGoプログラムを実行し、`GORACE=exitcode=13`を設定することで、プログラムが終了コード13を返すことを期待します。これにより、Race Detectorが指定された終了コードで終了する機能が正しく動作することを確認します。
* **`"strip_path_prefix"`**:
* `gorace`: `"atexit_sleep_ms=0 strip_path_prefix=/main."`
* **解説**: このテストケースは、`strip_path_prefix`パラメータの動作を検証します。データ競合が発生するGoプログラムを実行し、`GORACE=strip_path_prefix=/main.`を設定します。これにより、Race Detectorの出力に含まれるファイルパス(例: `main.go:7`)から、指定されたプレフィックス(`/main.`)が削除され、より簡潔なパス(例: `go:7`)が出力されることを期待します。正規表現パターンは、パスが短縮されていることを確認します。
* **`"halt_on_error"`**:
* `gorace`: `"atexit_sleep_ms=0 halt_on_error=1"`
* **解説**: このテストケースは、`halt_on_error`パラメータの動作を検証します。データ競合が発生するGoプログラムを実行し、`GORACE=halt_on_error=1`を設定します。これにより、Race Detectorが最初のデータ競合を検出した時点でプログラムが直ちに終了し、通常のRace Detectorの出力(`WARNING: DATA RACE`など)に加えて、`exit status 66`(デフォルトの終了コード)が返されることを期待します。これは、フェイルファストな挙動が正しく機能することを確認します。
これらのテストケースは、Go Race Detectorの`GORACE`環境変数の各パラメータが、期待される出力とプログラムの挙動をもたらすことを保証するための重要な追加です。
## 関連リンク
* [GitHub上のコミットページ](https://github.com/golang/go/commit/18f5ce856191062224d4bb08c7c2623296aeeaa2)
* [Go CL 13065043](https://golang.org/cl/13065043)
## 参考にした情報源リンク
* [Go Race Detector Documentation (go.dev)](https://go.dev/doc/articles/race_detector)
* [Go Race Detector Environment Variables (Stack Overflow)](https://stackoverflow.com/questions/24908709/go-race-detector-environment-variables)
* [Go Race Detector `halt_on_error` (Reddit)](https://www.reddit.com/r/golang/comments/101010/go_race_detector_haltonerror/)
* [Go Race Detector `atexit_sleep_ms` (Google Cloud Vertex AI Search)](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH666IBnIYB7O9PSnc3610YnPnp8HB1B40xpLpMM3pCO9Mekf-mUKwoa5QDSwaHfEhqbf8ZZY8T-9tFcRVzfyhTAnTSdCGrrCNCiLO6D-ATcvXd1l5wqIm-5AEzwL9ROGarEehbkW125nkhbAuqx9_TyjIZXJhVBRKUeAoojKoE)
* [Go Race Detector `strip_path_prefix` (Google Cloud Vertex AI Search)](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHikXeOLnzpHsFhHJPRHhZL7YO2ZaOAnNzuaj4L_Q9y8HLSeZvfjhX_4xlDOeXwrOtUxPkYSiIcLUsHMji5eA6In4tEXN_A3xJin5u_Wt8wS6ZtN9euj3a-45O3Fmd9LuHlJ4Oo)
* [Go Race Detector `exitcode` (Google Cloud Vertex AI Search)](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH6nZGBLaC0d34-u4o82VM6KUmiW512jnl-1SvEQ8sMyzzUs1FzRh67Igvov6p5dJC2QsZvZ3kSa7k-ZabkqpMOltL6kFwn6Bh4WQbob1S0lJwk525yEMGMlsA6HUSdEsrp148uNB_98wjYdSodoMN41V-E8sYT7YHMneGuwaVMNGhk03KbYeXnucH1jvzwWvClwOZxminlY0DSV8o=)
* [Go Race Detector `halt_on_error` (Google Cloud Vertex AI Search)](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE7yaUOM-_i_G5ShZ79iVtT8U6Jt0lxGPXbwwS08mGAThuaIDnqRtNrydlVcUoz82jpvLCW8YWk9M0vtuKfYNdPcDbEMp9a77MRVvv80_lB5-CK9R9lTCXVms552UpKmZGAlTe4mk_8rg4FO3HI1QQ8-dlXrr_YBQKl-uXGaM5v6ligr-NbFhQFuX1bWgHYoMwQ_MU7iyVFIiypEw==)