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

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

このコミットは、Go言語のdebug/gosymパッケージ内のテストコードpclntab_test.goに対する修正です。具体的には、テスト実行時に生成される一時的なバイナリファイルの保存場所を、共有マシン環境での問題発生を防ぐために、より安全な一時ディレクトリに変更し、テスト終了後に確実にクリーンアップするように改善しています。これにより、all.bashスクリプトの実行が共有環境で安定するようになります。

コミット

commit 494fe3b08fd78752497a1dc5838777dc4fb52650
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Mon Apr 9 11:19:52 2012 -0700

    debug/gosym: in test, use temp binary name in /tmp, and clean up.
    
    This fixes all.bash on shared machines.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5992078

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

https://github.com/golang/go/commit/494fe3b08fd78752497a1dc5838777dc4fb52650

元コミット内容

debug/gosym: in test, use temp binary name in /tmp, and clean up.

This fixes all.bash on shared machines.

変更の背景

この変更の背景には、Go言語のテストスイート全体を実行するall.bashスクリプトが、共有開発環境(複数の開発者が同じマシン上でテストを実行するような環境)で不安定になる問題がありました。

debug/gosymパッケージのテスト(特にpclntab_test.go)では、テストのために一時的な実行可能バイナリを生成し、それを使用してシンボル情報や行番号情報を解析していました。以前の実装では、この一時バイナリをos.TempDir()が返すシステム全体の一時ディレクトリ(例えば/tmp)の直下に固定の名前(pclinetest)で作成していました。

共有マシン環境では、複数のユーザーやプロセスが同時にテストを実行する可能性があります。この場合、同じ名前のファイルが同時に作成・アクセス・削除されることで、ファイル名の衝突、パーミッションの問題、あるいは他のテストプロセスが意図せずファイルを上書き・削除してしまうといった競合状態が発生し、テストが失敗する原因となっていました。

この問題を解決し、all.bashが共有環境でも安定して動作するようにするために、一時バイナリの生成方法とクリーンアップ処理の改善が必要とされました。

前提知識の解説

  • debug/gosymパッケージ: Go言語の標準ライブラリの一つで、Goバイナリに含まれるシンボルテーブル(go.buildidgo.symtabgo.pclntabセクションなど)を解析するための機能を提供します。特にpclntab(Program Counter Line Table)は、実行アドレスとソースコードの行番号のマッピング情報を含んでおり、デバッガやプロファイラが正確なスタックトレースやコードカバレッジ情報を提供するために不可欠です。
  • pclntab (Program Counter Line Table): Goバイナリに埋め込まれている重要なデータ構造で、プログラムカウンタ(PC)の値と、対応するソースファイル名、行番号、関数名などの情報を関連付けます。これにより、実行中のプログラムのどの部分がどのソースコードに対応しているかを特定できます。
  • go tool 6a: Go言語のアセンブラツールです。Goのソースコード(またはアセンブリコード)をオブジェクトファイルにコンパイルするために使用されます。このコミットでは、pclinetest.asmというアセンブリソースファイルをオブジェクトファイル(.6拡張子)に変換するために使われています。
  • go tool 6l: Go言語のリンカツールです。6aによって生成されたオブジェクトファイルや他のライブラリを結合し、最終的な実行可能バイナリを生成します。このコミットでは、pclinetest.6オブジェクトファイルからpclinetestという実行可能バイナリを生成するために使われています。
  • os.TempDir(): Go言語の標準ライブラリosパッケージの関数で、オペレーティングシステムが一時ファイルを保存するために推奨するデフォルトのディレクトリのパスを返します。Linuxでは通常/tmp、WindowsではC:\Users\<username>\AppData\Local\Tempなどが返されます。
  • ioutil.TempDir(dir, prefix): Go言語の標準ライブラリio/ioutilパッケージの関数(Go 1.16以降はos.MkdirTempに移行)で、指定されたディレクトリdir内に、指定されたprefixで始まる一意な名前の一時ディレクトリを作成します。この関数は、競合を避けるためにランダムな文字列を名前に含めるため、複数のプロセスが同時に一時ディレクトリを作成しても名前の衝突が起こりにくいという利点があります。
  • filepath.Join(elem...): Go言語の標準ライブラリpath/filepathパッケージの関数で、複数のパス要素を結合して、オペレーティングシステムに適した形式の単一のパスを生成します。これにより、パス区切り文字(/\)の扱いをOSに依存させずに安全にパスを構築できます。
  • all.bash: Go言語のプロジェクトでよく使われるシェルスクリプトで、Goのソースコード全体をビルドし、すべてのテストを実行するためのスクリプトです。開発者が変更を加えた際に、Goプロジェクト全体が正しく動作するかを確認するために使用されます。

技術的詳細

このコミットは、Go言語のテストにおける一時ファイルの取り扱いに関するベストプラクティスを示しています。

以前のコードでは、テスト用のバイナリpclinetestos.TempDir() + "/pclinetest"という形で作成していました。これは、システムの一時ディレクトリのルートに固定の名前でファイルを置くことを意味します。このアプローチは、単一のユーザーが単一のプロセスでテストを実行する場合には問題ありませんが、以下のような問題を引き起こします。

  1. ファイル名の衝突: 複数のテストプロセスが同時に実行された場合、すべてが同じ名前のファイルを作成しようとするため、ファイル名の衝突が発生し、os.Createos.Renameなどの操作が失敗する可能性があります。
  2. パーミッションの問題: あるプロセスが作成したファイルが、別のユーザーやグループのパーミッションによってアクセスできない、あるいは削除できないといった問題が発生する可能性があります。
  3. 不完全なクリーンアップ: テストがクラッシュしたり、予期せぬ終了をした場合、一時ファイルが削除されずに残り、ディスクスペースを消費したり、後続のテスト実行に影響を与えたりする可能性があります。特に、共有環境では、他のユーザーが残したゴミファイルが問題となることがあります。

このコミットでは、これらの問題を解決するために以下の変更を導入しています。

  • 一意な一時ディレクトリの作成: ioutil.TempDir("", "pclinetest")を使用して、pclinetestというプレフィックスを持つ一意な名前の一時ディレクトリを作成します。この関数は、ディレクトリ名にランダムな文字列を付加するため、複数のテストが同時に実行されてもディレクトリ名の衝突が起こる可能性が極めて低くなります。
  • 一時ディレクトリ内でのバイナリ生成: 生成された一意な一時ディレクトリのパスとpclinetestというバイナリ名をfilepath.Joinで結合し、一時バイナリの完全なパスを構築します。これにより、バイナリが他のテストやシステム上のファイルと衝突するのを防ぎます。
  • 確実なクリーンアップ: defer os.RemoveAll(pclineTempDir)TestPCLine関数の冒頭に追加しています。deferステートメントは、その関数がリターンする直前に指定された関数を実行することを保証します。os.RemoveAllは指定されたパスのファイルまたはディレクトリとその内容をすべて削除するため、テストが正常終了しても、パニックで終了しても、一時ディレクトリとその中のバイナリが確実に削除され、クリーンアップが保証されます。

これらの変更により、pclntab_test.goのテストは共有マシン環境でも安定して実行できるようになり、all.bashスクリプトの信頼性が向上しました。

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

--- a/src/pkg/debug/gosym/pclntab_test.go
+++ b/src/pkg/debug/gosym/pclntab_test.go
@@ -7,14 +7,19 @@ package gosym
 import (
 	"debug/elf"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"os/exec"
+	"path/filepath"
 	"runtime"
 	"strings"
 	"testing"
 )
 
-var pclinetestBinary string
+var (
+	pclineTempDir    string
+	pclinetestBinary string
+)
 
 func dotest() bool {
 	// For now, only works on ELF platforms.
@@ -24,10 +29,18 @@ func dotest() bool {
 	if pclinetestBinary != "" {
 		return true
 	}
+	var err error
+	pclineTempDir, err = ioutil.TempDir("", "pclinetest")
+	if err != nil {
+		panic(err)
+	}
+	if strings.Contains(pclineTempDir, " ") {
+		panic("unexpected space in tempdir")
+	}
 	// This command builds pclinetest from pclinetest.asm;
 	// the resulting binary looks like it was built from pclinetest.s,
 	// but we have renamed it to keep it away from the go tool.
-	pclinetestBinary = os.TempDir() + "/pclinetest"
+	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
 	command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
 		pclinetestBinary, pclinetestBinary, pclinetestBinary)
 	cmd := exec.Command("sh", "-c", command)
@@ -170,6 +183,7 @@ func TestPCLine(t *testing.T) {
 	if !dotest() {
 		return
 	}
+	defer os.RemoveAll(pclineTempDir)
 
 	f, tab := crack(pclinetestBinary, t)
 	text := f.Section(".text")

コアとなるコードの解説

  1. インポートの追加:

    • "io/ioutil": 一時ディレクトリを作成するためにioutil.TempDir関数を使用するために追加されました。
    • "path/filepath": パスをOSに依存しない形で結合するためにfilepath.Join関数を使用するために追加されました。
  2. グローバル変数の変更:

    • var pclinetestBinary stringが、var ( pclineTempDir string pclinetestBinary string )に変更されました。
    • pclineTempDirという新しいグローバル変数が追加され、作成された一時ディレクトリのパスを保持するために使用されます。
  3. 一時ディレクトリの作成とパスの構築:

    • pclinetestBinary = os.TempDir() + "/pclinetest"という行が削除されました。
    • 代わりに、dotest()関数内で以下の処理が追加されました。
      var err error
      pclineTempDir, err = ioutil.TempDir("", "pclinetest")
      if err != nil {
          panic(err)
      }
      if strings.Contains(pclineTempDir, " ") {
          panic("unexpected space in tempdir")
      }
      pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
      
      • ioutil.TempDir("", "pclinetest")を呼び出して、システムの一時ディレクトリ内にpclinetestというプレフィックスを持つ一意な名前の一時ディレクトリを作成し、そのパスをpclineTempDirに格納します。
      • エラーハンドリングが追加され、一時ディレクトリの作成に失敗した場合はパニックします。
      • 作成された一時ディレクトリのパスにスペースが含まれていないかを確認するアサーションが追加されています。これは、シェルコマンドでパスを扱う際にスペースが問題を引き起こす可能性があるためです。
      • filepath.Join(pclineTempDir, "pclinetest")を使用して、作成された一時ディレクトリのパスとバイナリ名pclinetestを結合し、最終的なバイナリのフルパスをpclinetestBinaryに設定します。これにより、バイナリは一意な一時ディレクトリ内に配置されることが保証されます。
  4. クリーンアップ処理の追加:

    • TestPCLine関数の冒頭にdefer os.RemoveAll(pclineTempDir)が追加されました。
    • このdeferステートメントにより、TestPCLine関数が終了する際に(正常終了でもエラー終了でも)、pclineTempDirで指定された一時ディレクトリとその内容(生成されたpclinetestバイナリを含む)が確実に削除されます。これにより、テスト実行後に不要なファイルがシステムに残ることを防ぎ、共有環境でのディスクスペースの消費や競合の問題を解消します。

これらの変更により、テストはより堅牢になり、共有開発環境でのall.bashの安定性が向上しました。

関連リンク

参考にした情報源リンク

このコミットは、Go言語のdebug/gosymパッケージ内のテストコードpclntab_test.goに対する修正です。具体的には、テスト実行時に生成される一時的なバイナリファイルの保存場所を、共有マシン環境での問題発生を防ぐために、より安全な一時ディレクトリに変更し、テスト終了後に確実にクリーンアップするように改善しています。これにより、all.bashスクリプトの実行が共有環境で安定するようになります。

コミット

commit 494fe3b08fd78752497a1dc5838777dc4fb52650
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Mon Apr 9 11:19:52 2012 -0700

    debug/gosym: in test, use temp binary name in /tmp, and clean up.
    
    This fixes all.bash on shared machines.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5992078

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

https://github.com/golang/go/commit/494fe3b08fd78752497a1dc5838777dc4fb52650

元コミット内容

debug/gosym: in test, use temp binary name in /tmp, and clean up.

This fixes all.bash on shared machines.

変更の背景

この変更の背景には、Go言語のテストスイート全体を実行するall.bashスクリプトが、共有開発環境(複数の開発者が同じマシン上でテストを実行するような環境)で不安定になる問題がありました。

debug/gosymパッケージのテスト(特にpclntab_test.go)では、テストのために一時的な実行可能バイナリを生成し、それを使用してシンボル情報や行番号情報を解析していました。以前の実装では、この一時バイナリをos.TempDir()が返すシステム全体の一時ディレクトリ(例えば/tmp)の直下に固定の名前(pclinetest)で作成していました。

共有マシン環境では、複数のユーザーやプロセスが同時にテストを実行する可能性があります。この場合、同じ名前のファイルが同時に作成・アクセス・削除されることで、ファイル名の衝突、パーミッションの問題、あるいは他のテストプロセスが意図せずファイルを上書き・削除してしまうといった競合状態が発生し、テストが失敗する原因となっていました。

この問題を解決し、all.bashが共有環境でも安定して動作するようにするために、一時バイナリの生成方法とクリーンアップ処理の改善が必要とされました。

前提知識の解説

  • debug/gosymパッケージ: Go言語の標準ライブラリの一つで、Goバイナリに含まれるシンボルテーブル(go.buildidgo.symtabgo.pclntabセクションなど)を解析するための機能を提供します。特にpclntab(Program Counter Line Table)は、実行アドレスとソースコードの行番号のマッピング情報を含んでおり、デバッガやプロファイラが正確なスタックトレースやコードカバレッジ情報を提供するために不可欠です。
  • pclntab (Program Counter Line Table): Goバイナリに埋め込まれている重要なデータ構造で、プログラムカウンタ(PC)の値と、対応するソースファイル名、行番号、関数名などの情報を関連付けます。これにより、実行中のプログラムのどの部分がどのソースコードに対応しているかを特定できます。
  • go tool 6a: Go言語のアセンブラツールです。Goのソースコード(またはアセンブリコード)をオブジェクトファイルにコンパイルするために使用されます。このコミットでは、pclinetest.asmというアセンブリソースファイルをオブジェクトファイル(.6拡張子)に変換するために使われています。
  • go tool 6l: Go言語のリンカツールです。6aによって生成されたオブジェクトファイルや他のライブラリを結合し、最終的な実行可能バイナリを生成します。このコミットでは、pclinetest.6オブジェクトファイルからpclinetestという実行可能バイナリを生成するために使われています。
  • os.TempDir(): Go言語の標準ライブラリosパッケージの関数で、オペレーティングシステムが一時ファイルを保存するために推奨するデフォルトのディレクトリのパスを返します。Linuxでは通常/tmp、WindowsではC:\Users\<username>\AppData\Local\Tempなどが返されます。
  • ioutil.TempDir(dir, prefix): Go言語の標準ライブラリio/ioutilパッケージの関数(Go 1.16以降はos.MkdirTempに移行)で、指定されたディレクトリdir内に、指定されたprefixで始まる一意な名前の一時ディレクトリを作成します。この関数は、競合を避けるためにランダムな文字列を名前に含めるため、複数のプロセスが同時に一時ディレクトリを作成しても名前の衝突が起こりにくいという利点があります。
  • filepath.Join(elem...): Go言語の標準ライブラリpath/filepathパッケージの関数で、複数のパス要素を結合して、オペレーティングシステムに適した形式の単一のパスを生成します。これにより、パス区切り文字(/\)の扱いをOSに依存させずに安全にパスを構築できます。
  • all.bash: Go言語のプロジェクトでよく使われるシェルスクリプトで、Goのソースコード全体をビルドし、すべてのテストを実行するためのスクリプトです。開発者が変更を加えた際に、Goプロジェクト全体が正しく動作するかを確認するために使用されます。

技術的詳細

このコミットは、Go言語のテストにおける一時ファイルの取り扱いに関するベストプラクティスを示しています。

以前のコードでは、テスト用のバイナリpclinetestos.TempDir() + "/pclinetest"という形で作成していました。これは、システムの一時ディレクトリのルートに固定の名前でファイルを置くことを意味します。このアプローチは、単一のユーザーが単一のプロセスでテストを実行する場合には問題ありませんが、以下のような問題を引き起こします。

  1. ファイル名の衝突: 複数のテストプロセスが同時に実行された場合、すべてが同じ名前のファイルを作成しようとするため、ファイル名の衝突が発生し、os.Createos.Renameなどの操作が失敗する可能性があります。
  2. パーミッションの問題: あるプロセスが作成したファイルが、別のユーザーやグループのパーミッションによってアクセスできない、あるいは削除できないといった問題が発生する可能性があります。
  3. 不完全なクリーンアップ: テストがクラッシュしたり、予期せぬ終了をした場合、一時ファイルが削除されずに残り、ディスクスペースを消費したり、後続のテスト実行に影響を与えたりする可能性があります。特に、共有環境では、他のユーザーが残したゴミファイルが問題となることがあります。

このコミットでは、これらの問題を解決するために以下の変更を導入しています。

  • 一意な一時ディレクトリの作成: ioutil.TempDir("", "pclinetest")を使用して、pclinetestというプレフィックスを持つ一意な名前の一時ディレクトリを作成します。この関数は、ディレクトリ名にランダムな文字列を付加するため、複数のテストが同時に実行されてもディレクトリ名の衝突が起こる可能性が極めて低くなります。
  • 一時ディレクトリ内でのバイナリ生成: 生成された一意な一時ディレクトリのパスとpclinetestというバイナリ名をfilepath.Joinで結合し、一時バイナリの完全なパスを構築します。これにより、バイナリが他のテストやシステム上のファイルと衝突するのを防ぎます。
  • 確実なクリーンアップ: defer os.RemoveAll(pclineTempDir)TestPCLine関数の冒頭に追加しています。deferステートメントは、その関数がリターンする直前に指定された関数を実行することを保証します。os.RemoveAllは指定されたパスのファイルまたはディレクトリとその内容をすべて削除するため、テストが正常終了しても、パニックで終了しても、一時ディレクトリとその中のバイナリが確実に削除され、クリーンアップが保証されます。

これらの変更により、pclntab_test.goのテストは共有マシン環境でも安定して実行できるようになり、all.bashスクリプトの信頼性が向上しました。

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

--- a/src/pkg/debug/gosym/pclntab_test.go
+++ b/src/pkg/debug/gosym/pclntab_test.go
@@ -7,14 +7,19 @@ package gosym
 import (
 	"debug/elf"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"os/exec"
+	"path/filepath"
 	"runtime"
 	"strings"
 	"testing"
 )
 
-var pclinetestBinary string
+var (
+	pclineTempDir    string
+	pclinetestBinary string
+)
 
 func dotest() bool {
 	// For now, only works on ELF platforms.
@@ -24,10 +29,18 @@ func dotest() bool {
 	if pclinetestBinary != "" {
 		return true
 	}
+	var err error
+	pclineTempDir, err = ioutil.TempDir("", "pclinetest")
+	if err != nil {
+		panic(err)
+	}
+	if strings.Contains(pclineTempDir, " ") {
+		panic("unexpected space in tempdir")
+	}
 	// This command builds pclinetest from pclinetest.asm;
 	// the resulting binary looks like it was built from pclinetest.s,
 	// but we have renamed it to keep it away from the go tool.
-	pclinetestBinary = os.TempDir() + "/pclinetest"
+	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
 	command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
 		pclinetestBinary, pclinetestBinary, pclinetestBinary)
 	cmd := exec.Command("sh", "-c", command)
@@ -170,6 +183,7 @@ func TestPCLine(t *testing.T) {\n \tif !dotest() {\n \t\treturn\n \t}\n+\tdefer os.RemoveAll(pclineTempDir)\n \n \tf, tab := crack(pclinetestBinary, t)\n \ttext := f.Section(\".text\")\n```

## コアとなるコードの解説

1.  **インポートの追加**:
    *   `"io/ioutil"`: 一時ディレクトリを作成するために`ioutil.TempDir`関数を使用するために追加されました。
    *   `"path/filepath"`: パスをOSに依存しない形で結合するために`filepath.Join`関数を使用するために追加されました。

2.  **グローバル変数の変更**:
    *   `var pclinetestBinary string`が、`var ( pclineTempDir string pclinetestBinary string )`に変更されました。
    *   `pclineTempDir`という新しいグローバル変数が追加され、作成された一時ディレクトリのパスを保持するために使用されます。

3.  **一時ディレクトリの作成とパスの構築**:
    *   `pclinetestBinary = os.TempDir() + "/pclinetest"`という行が削除されました。
    *   代わりに、`dotest()`関数内で以下の処理が追加されました。
        ```go
        var err error
        pclineTempDir, err = ioutil.TempDir("", "pclinetest")
        if err != nil {
            panic(err)
        }
        if strings.Contains(pclineTempDir, " ") {
            panic("unexpected space in tempdir")
        }
        pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
        ```
        *   `ioutil.TempDir("", "pclinetest")`を呼び出して、システムの一時ディレクトリ内に`pclinetest`というプレフィックスを持つ一意な名前の一時ディレクトリを作成し、そのパスを`pclineTempDir`に格納します。
        *   エラーハンドリングが追加され、一時ディレクトリの作成に失敗した場合はパニックします。
        *   作成された一時ディレクトリのパスにスペースが含まれていないかを確認するアサーションが追加されています。これは、シェルコマンドでパスを扱う際にスペースが問題を引き起こす可能性があるためです。
        *   `filepath.Join(pclineTempDir, "pclinetest")`を使用して、作成された一時ディレクトリのパスとバイナリ名`pclinetest`を結合し、最終的なバイナリのフルパスを`pclinetestBinary`に設定します。これにより、バイナリは一意な一時ディレクトリ内に配置されることが保証されます。

4.  **クリーンアップ処理の追加**:
    *   `TestPCLine`関数の冒頭に`defer os.RemoveAll(pclineTempDir)`が追加されました。
    *   この`defer`ステートメントにより、`TestPCLine`関数が終了する際に(正常終了でもエラー終了でも)、`pclineTempDir`で指定された一時ディレクトリとその内容(生成された`pclinetest`バイナリを含む)が確実に削除されます。これにより、テスト実行後に不要なファイルがシステムに残ることを防ぎ、共有環境でのディスクスペースの消費や競合の問題を解消します。

これらの変更により、テストはより堅牢になり、共有開発環境での`all.bash`の安定性が向上しました。

## 関連リンク

*   Go CL 5992078: [https://golang.org/cl/5992078](https://golang.org/cl/5992078)

## 参考にした情報源リンク

*   Go言語 `debug/gosym` パッケージ: [https://pkg.go.dev/debug/gosym](https://pkg.go.dev/debug/gosym)
*   Go言語 `os` パッケージ: [https://pkg.go.dev/os](https://pkg.go.dev/os)
*   Go言語 `io/ioutil` パッケージ (Go 1.16以降は`os.MkdirTemp`): [https://pkg.go.dev/io/ioutil](https://pkg.go.dev/io/ioutil)
*   Go言語 `path/filepath` パッケージ: [https://pkg.go.dev/path/filepath](https://pkg.go.dev/path/filepath)
*   Go言語 `defer` ステートメント: [https://go.dev/blog/defer-panic-recover](https://go.dev/blog/defer-panic-recover)
*   Go言語のビルドツール (`go tool 6a`, `go tool 6l`): Goのドキュメントや関連するブログ記事を参照。例: [https://go.dev/doc/](https://go.dev/doc/)
*   Go言語のテストと`all.bash`に関する一般的な情報: Goの公式ドキュメントやコミュニティの議論を参照。