Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 16019] ファイルの概要

このコミットは、Go言語の標準ライブラリである os/exec および syscall パッケージ内のテストが、GOGCTRACE=1 環境変数が設定されている場合に失敗する問題を修正するものです。具体的には、テストヘルパープロセスを起動する際に、親プロセスの環境変数をすべて引き継ぐのではなく、必要な環境変数のみを明示的に設定するように変更することで、この問題を解決しています。

コミット

commit 0399e76b6673080b88a540ad669cfcf638078d7a
Author: Albert Strasheim <fullung@gmail.com>
Date:   Sat Mar 30 09:22:11 2013 -0700

    os/exec, syscall: fix tests to pass when GOGCTRACE=1 is set
    
    R=golang-dev, bradfitz, minux.ma
    CC=golang-dev
    https://golang.org/cl/8193043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/0399e76b6673080b88a540ad669cfcf638078d7a

元コミット内容

os/exec, syscall: fix tests to pass when GOGCTRACE=1 is set

変更の背景

Go言語のテストフレームワークでは、特定のテストシナリオのために「ヘルパープロセス」を起動することがよくあります。これは、os.Args[0] を使って自分自身を再実行し、特定の環境変数(例: GO_WANT_HELPER_PROCESS=1)をチェックすることで、ヘルパープロセスとしての振る舞いを制御する手法です。

このコミットが行われる以前は、これらのヘルパープロセスは親プロセスの環境変数すべてを継承していました。しかし、GOGCTRACE=1 という環境変数が設定されている場合、Goランタイムはガベージコレクション(GC)のトレース情報を標準エラー出力(stderr)に出力します。テストヘルパープロセスが親プロセスの環境変数をそのまま引き継ぐと、GOGCTRACE=1 も引き継がれ、ヘルパープロセスがGCトレース情報をstderrに出力してしまいます。

多くのテストでは、cmd.CombinedOutput()cmd.Output() を使用して、コマンドの標準出力と標準エラー出力をキャプチャし、その内容を検証します。ヘルパープロセスが予期せぬGCトレース情報をstderrに出力すると、テストが期待する出力と異なり、テストが失敗する原因となっていました。このコミットは、この問題を解決するために、ヘルパープロセスに渡す環境変数を明示的に制御するように変更しました。

前提知識の解説

  • os/exec パッケージ: Go言語で外部コマンドを実行するためのパッケージです。exec.Command 関数を使ってコマンドを構築し、Run, Output, CombinedOutput などのメソッドで実行します。
  • syscall パッケージ: オペレーティングシステムが提供する低レベルなシステムコールにアクセスするためのパッケージです。ファイルディスクリプタの受け渡し(passfd)など、OS固有の機能を利用する際に使用されます。
  • 環境変数: オペレーティングシステムがプロセスに提供する動的な名前付き値です。プロセスは環境変数を通じて設定情報や動作モードを受け取ることができます。
  • os.Environ(): Go言語の os パッケージにある関数で、現在のプロセスの環境変数を key=value 形式の文字列スライスとして返します。
  • GOGCTRACE 環境変数: Goランタイムのガベージコレクションの動作を制御するための環境変数の一つです。GOGCTRACE=1 を設定すると、GCの実行時に詳細なトレース情報が標準エラー出力に表示されます。これはデバッグやパフォーマンスチューニングに役立ちますが、プログラムの通常の出力には含まれない情報です。
  • テストヘルパープロセス: Goのテストにおいて、メインのテストプロセスとは別に、特定の機能やコマンドの実行をシミュレートするために起動される補助的なプロセスです。os.Args[0] を使ってテストバイナリ自身を再実行し、特定の環境変数やコマンドライン引数に基づいて異なる動作をさせることが一般的です。

技術的詳細

この問題の根本原因は、Goの os/exec パッケージで外部コマンド(この場合はテストヘルパープロセス)を起動する際に、デフォルトで親プロセスの環境変数をすべて子プロセスに引き継ぐ動作にありました。

以前のコードでは、cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...) のように記述されていました。これは、GO_WANT_HELPER_PROCESS=1 という環境変数を追加し、その後に os.Environ() で取得した親プロセスのすべての環境変数を結合して、子プロセスの環境変数として設定するという意味です。

この方法では、親プロセスで GOGCTRACE=1 が設定されていると、その情報もヘルパープロセスに引き継がれてしまいます。結果として、ヘルパープロセスがGCトレース情報をstderrに出力し、テストの出力検証を妨げていました。

修正後のコードでは、cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"} と変更されています。これにより、ヘルパープロセスに渡される環境変数は GO_WANT_HELPER_PROCESS=1 のみとなり、親プロセスの他の環境変数(GOGCTRACE=1 など)は引き継がれなくなります。これにより、ヘルパープロセスが余計なGCトレース情報を出力することがなくなり、テストが正しくパスするようになりました。

この変更は、テストの独立性を高めるという観点からも重要です。テストヘルパープロセスは、そのテストに必要な最小限の環境で実行されるべきであり、親プロセスの環境に依存しすぎない方が、テストの信頼性と再現性が向上します。

コアとなるコードの変更箇所

--- a/src/pkg/os/exec/exec_test.go
+++ b/src/pkg/os/exec/exec_test.go
@@ -25,7 +25,7 @@ func helperCommand(s ...string) *Cmd {
 	cs := []string{"-test.run=TestHelperProcess", "--"}
 	cs = append(cs, s...)
 	cmd := Command(os.Args[0], cs...)
-	cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
+	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
 	return cmd
 }
 
--- a/src/pkg/syscall/passfd_test.go
+++ b/src/pkg/syscall/passfd_test.go
@@ -49,7 +49,7 @@ func TestPassFD(t *testing.T) {
 	defer readFile.Close()
 
 	cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir)
-	cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)
+	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
 	cmd.ExtraFiles = []*os.File{writeFile}
 
 	out, err := cmd.CombinedOutput()

コアとなるコードの解説

変更は src/pkg/os/exec/exec_test.gosrc/pkg/syscall/passfd_test.go の2つのファイルで行われています。どちらのファイルも、テストヘルパープロセスを起動する部分の cmd.Env の設定が変更されています。

変更前:

cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS=1"}, os.Environ()...)

この行は、GO_WANT_HELPER_PROCESS=1 という新しい環境変数を要素とするスライスを作成し、その後に os.Environ() が返す現在のプロセスのすべての環境変数をそのスライスに追加しています。結果として、子プロセスは親プロセスの環境変数をすべて継承し、それに加えて GO_WANT_HELPER_PROCESS が設定されます。

変更後:

cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}

この行は、子プロセスの環境変数として GO_WANT_HELPER_PROCESS=1 のみを含む新しい文字列スライスを直接設定しています。これにより、親プロセスの他の環境変数は子プロセスに引き継がれなくなります。特に、GOGCTRACE=1 のようなテストの出力に影響を与える可能性のある環境変数が引き継がれることを防ぎ、テストの安定性を向上させています。

この変更は、テストの実行環境をより厳密に制御し、外部要因(この場合は GOGCTRACE 環境変数)によるテストの不安定性を排除することを目的としています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコードリポジトリ (GitHub)
  • Go言語のIssueトラッカーやメーリングリストでの関連議論 (golang-dev)
  • Stack OverflowなどのプログラミングQ&Aサイトでの関連情報