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

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

このコミットは、Go言語の標準ライブラリをデータ競合検出器(Race Detector)を用いてテストするためのシェルスクリプト race.bashsrc ディレクトリに追加するものです。このスクリプトは、適切なハードウェアを持つユーザーがデータ競合検出器を有効にしたビルドを実行し、標準ライブラリのテストを行うことを可能にします。また、Goプロジェクトのダッシュボードビルダーから呼び出されることも想定されています。

コミット

このコミットは、Go言語のソースツリーに race.bash という新しいシェルスクリプトを追加します。このスクリプトは、Goのデータ競合検出器を使用して標準ライブラリのテストを実行するためのもので、特に linux/amd64 および darwin/amd64 環境でのみサポートされます。スクリプトは、実行環境のOSとアーキテクチャをチェックし、$GOROOT/src から実行されていることを確認した後、make.bash を実行し、go install -race std および go test -race std コマンドを通じてデータ競合検出器を有効にしたビルドとテストを行います。

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

https://github.com/golang/go/commit/b53e95ac2efa287d5932acc4a2dbf68bdb2a5659

元コミット内容

src: add race.bash

Add race.bash so anyone with suitable hardware can run a race detector build. race.bash can be called from the dashboard builder by passing -cmd="race.bash".

Original source for race.bash is here, http://code.google.com/p/go-wiki/wiki/DashboardBuilders

TODO: add race.bat for windows/amd64

R=dvyukov, minux.ma, adg, rsc
CC=fullung, golang-dev
https://golang.org/cl/7179052

変更の背景

この変更の背景には、Go言語の並行処理モデルにおけるデータ競合(data race)の問題を早期に発見し、解決する必要性がありました。データ競合は、複数のゴルーチンが同時に同じメモリ位置にアクセスし、少なくとも1つのアクセスが書き込みである場合に発生するバグであり、プログラムの予測不能な動作やクラッシュを引き起こす可能性があります。

Go 1.1から導入されたデータ競合検出器は、このような問題をコンパイル時や実行時に検出するための強力なツールです。しかし、この検出器を有効にしたビルドやテストは、通常のビルドやテストよりも多くのリソース(CPU、メモリ)を消費するため、特定の環境や目的でのみ実行されることが一般的でした。

race.bash スクリプトの追加は、以下の目的を達成するために行われました。

  1. データ競合検出器の利用促進: 開発者が自身の環境で簡単にデータ競合検出器を有効にしたビルドとテストを実行できるようにすることで、データ競合バグの早期発見と修正を促します。
  2. ダッシュボードビルダーとの統合: Goプロジェクトの継続的インテグレーション(CI)システムであるダッシュボードビルダーから race.bash を呼び出すことで、自動的にデータ競合検出器を有効にしたテストを実行し、プロジェクト全体の品質を向上させます。これにより、Go標準ライブラリ自体にデータ競合が存在しないことを継続的に保証できるようになります。
  3. 特定の環境への対応: データ競合検出器が linux/amd64darwin/amd64 でのみサポートされていたため、これらの環境に特化したスクリプトを提供することで、利用者が適切な環境で確実に検出器を利用できるようにします。

このスクリプトは、Goコミュニティが並行処理の安全性を高めるための継続的な取り組みの一環として追加されました。

前提知識の解説

このコミットとスクリプトを理解するためには、以下の前提知識が必要です。

1. Go言語のデータ競合検出器 (Race Detector)

Go言語のデータ競合検出器は、Go 1.1で導入された強力なツールで、並行処理におけるデータ競合バグを検出します。データ競合は、以下の3つの条件がすべて満たされたときに発生します。

  • 少なくとも2つのゴルーチンが同じメモリ位置に同時にアクセスする。
  • 少なくとも1つのアクセスが書き込みである。
  • アクセスが同期メカニズム(ミューテックス、チャネルなど)によって保護されていない。

データ競合検出器は、プログラムの実行中にメモリアクセスを監視し、上記の条件が満たされた場合に警告を出力します。これにより、開発者は並行処理のバグを特定し、修正することができます。

データ競合検出器を有効にするには、go buildgo rungo test コマンドに -race フラグを追加します。例えば、go test -race ./... は、現在のモジュール内のすべてのパッケージのテストをデータ競合検出器を有効にして実行します。

2. make.bash

make.bash は、Go言語のソースコードリポジトリの src ディレクトリに存在するシェルスクリプトです。これは、Goのツールチェイン全体をビルドするために使用される主要なスクリプトです。GoのソースコードからGoコンパイラ、標準ライブラリ、およびその他のツールを構築する複雑なプロセスを自動化します。

通常、Goのソースコードをクローンした後、cd $GOROOT/src に移動し、./make.bash を実行することで、Goのビルド環境をセットアップします。このスクリプトは、Goのビルドシステムの中核をなすものであり、Goのバージョンアップやクロスコンパイル環境の構築など、様々な場面で利用されます。

race.bash スクリプト内で ./make.bash --no-banner が呼び出されているのは、データ競合検出器を有効にしたビルドを行う前に、Goのビルド環境が正しく設定されていることを確認し、必要に応じて再構築するためです。--no-banner オプションは、make.bash が通常出力するビルド情報のバナーを抑制します。

3. go install -race std

このコマンドは、Goの標準ライブラリ(std)をデータ競合検出器を有効にしてインストールします。go install コマンドは、指定されたパッケージをコンパイルし、その結果を $GOPATH/bin または $GOBIN に実行可能ファイルとして、または $GOPATH/pkg にパッケージアーカイブとしてインストールします。

-race フラグを付けることで、コンパイルされた標準ライブラリのバイナリにデータ競合検出器のランタイムサポートが組み込まれます。これにより、この標準ライブラリを使用するアプリケーションが実行される際に、データ競合が検出されるようになります。

4. go test -race

このコマンドは、指定されたパッケージのテストをデータ競合検出器を有効にして実行します。go test コマンドは、Goのテストフレームワークを実行するための主要なコマンドです。

race.bash スクリプトでは、以下の3つの go test -race コマンドが実行されます。

  • go test -race -short std: 標準ライブラリのテストをデータ競合検出器を有効にして実行します。-short フラグは、時間がかかるテストをスキップし、より迅速なテスト実行を可能にします。
  • go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std: 標準ライブラリのベンチマークテストをデータ競合検出器を有効にして実行します。
    • -run=nothingplease: 通常のテストは実行せず、ベンチマークのみを実行するためのトリックです。nothingplease という名前のテスト関数は存在しないため、テストはスキップされます。
    • -bench=.*: すべてのベンチマークを実行します。
    • -benchtime=.1s: 各ベンチマークの実行時間を0.1秒に制限します。これにより、ベンチマークテストの実行時間を短縮します。
    • -cpu=4: ベンチマークテストを4つのCPUコアで並行して実行します。

これらのテストコマンドは、標準ライブラリがデータ競合を起こさないことを確認するために、様々なシナリオでデータ競合検出器を適用しています。

5. シェルスクリプトの基本

  • #!/usr/bin/env bash: シバン(shebang)と呼ばれ、このスクリプトが bash シェルで実行されることを指定します。
  • set -e: このコマンドは、スクリプト内でエラーが発生した場合(コマンドがゼロ以外の終了ステータスを返した場合)に、スクリプトの実行を即座に終了させるように設定します。これにより、予期せぬエラーが後続の処理に影響を与えるのを防ぎます。
  • function usage { ... }: usage という名前の関数を定義します。この関数は、スクリプトの利用方法やエラーメッセージを表示するために使用されます。
  • echo '...' 1>&2: 標準エラー出力(stderr)にメッセージを出力します。1>&2 は、標準出力(ファイルディスクリプタ1)を標準エラー出力(ファイルディスクリプタ2)にリダイレクトすることを意味します。
  • exit 1: スクリプトを終了し、終了ステータスとして1を返します。通常、ゼロ以外の終了ステータスはエラーを示します。
  • case $(uname) in ... esac: uname コマンドの出力(オペレーティングシステム名)に基づいて条件分岐を行います。Darwin はmacOS、Linux はLinuxを指します。
  • sysctl machdep.cpu.extfeatures | grep -qv EM64T: macOS環境で、CPUがEM64T(Intel 64)をサポートしているかを確認します。sysctl はシステム情報を取得するコマンド、grep -qv はパターンに一致しない行を非表示で検索します。
  • [ $(uname -m) != "x86_64" ]: Linux環境で、CPUアーキテクチャが x86_64(64ビット)でないことを確認します。
  • [ ! -f make.bash ]: make.bash というファイルが存在しないことを確認します。

これらの基本的なシェルスクリプトの知識は、race.bash の動作を理解するために不可欠です。

技術的詳細

race.bash スクリプトは、Goのデータ競合検出器を効果的に利用するための堅牢なシェルスクリプトであり、以下の技術的詳細を含んでいます。

1. 環境チェックとエラーハンドリング

スクリプトの冒頭で set -e が宣言されており、これによりコマンドが失敗した場合(非ゼロの終了コードを返した場合)にスクリプトが即座に終了するようになります。これは、ビルドやテストの途中でエラーが発生した場合に、不完全な結果で処理を続行するのを防ぐための重要な安全策です。

また、usage 関数が定義されており、サポートされていない環境でスクリプトが実行された場合に、適切なエラーメッセージを表示して終了します。

2. OSとアーキテクチャの検証

データ競合検出器は、特定のOSとアーキテクチャの組み合わせでのみサポートされています。race.bash は、uname コマンドを使用して現在のOSとアーキテクチャをチェックし、サポートされていない環境であれば usage 関数を呼び出して終了します。

  • macOS (Darwin):
    • sysctl machdep.cpu.extfeatures | grep -qv EM64T: macOSでは、sysctl コマンドを使ってCPUの拡張機能を確認し、EM64T(Intel 64ビットアーキテクチャ)がサポートされていることを確認します。grep -qv EM64T は、EM64T が見つからない場合に真となり、その場合は usage を呼び出します。これは、macOSがIntelベースの64ビットCPUで実行されていることを確認するためのものです。
  • Linux:
    • [ $(uname -m) != "x86_64" ]: Linuxでは、uname -m コマンドを使ってマシンアーキテクチャが x86_64(64ビット)であることを確認します。x86_64 でない場合は usage を呼び出します。

これらのチェックにより、スクリプトはデータ競合検出器が動作する保証された環境でのみ実行されるようになります。

3. 実行パスの検証

race.bash は、Goのソースツリーの src ディレクトリから実行されることを前提としています。これは、make.bash が同じディレクトリに存在することを期待しているためです。

  • if [ ! -f make.bash ]; then ... fi: この条件文は、現在のディレクトリに make.bash というファイルが存在しない場合に真となります。その場合、スクリプトはエラーメッセージを出力し、終了します。これにより、ユーザーが誤った場所でスクリプトを実行するのを防ぎます。

4. Goビルド環境の準備

  • . ./make.bash --no-banner: この行は、make.bash スクリプトを現在のシェルでソース(実行)します。これにより、make.bash が設定する環境変数(例: GOROOT)が race.bash の現在のシェルセッションに引き継がれます。--no-banner オプションは、make.bash が通常出力するビルド情報のバナーを抑制し、スクリプトの出力をクリーンに保ちます。このステップは、データ競合検出器を有効にしたビルドを行う前に、Goのビルド環境が正しく設定されていることを保証するために重要です。

5. データ競合検出器を有効にしたビルドとテストの実行

スクリプトの核心部分は、データ競合検出器を有効にしたGoコマンドの実行です。

  • go install -race std: Goの標準ライブラリ(std)をデータ競合検出器を有効にしてコンパイルし、インストールします。これにより、標準ライブラリのバイナリにデータ競合検出器のランタイムサポートが組み込まれ、後続のテストでデータ競合が検出されるようになります。
  • go test -race -short std: 標準ライブラリのテストをデータ競合検出器を有効にして実行します。-short フラグは、時間がかかるテストをスキップし、より迅速なテスト実行を可能にします。これは、日常的なCIや開発サイクルでの利用に適しています。
  • go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std: 標準ライブラリのベンチマークテストをデータ競合検出器を有効にして実行します。
    • -run=nothingplease: 存在しないテスト名を指定することで、通常のテストの実行をスキップします。
    • -bench=.*: すべてのベンチマークを実行対象とします。
    • -benchtime=.1s: 各ベンチマークの実行時間を0.1秒に制限します。これにより、ベンチマークテストの実行時間を短縮し、CI環境での実行に適した時間で完了させます。
    • -cpu=4: ベンチマークテストを4つのCPUコアで並行して実行します。これにより、並行処理のシナリオでのデータ競合の検出可能性を高めます。

これらのコマンドの組み合わせにより、race.bash はGo標準ライブラリのデータ競合を包括的にテストし、潜在的な問題を特定するための強力なメカニズムを提供します。

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

このコミットによるコアとなるコードの変更箇所は、src/race.bash という新しいファイルが追加されたことです。

--- /dev/null
+++ b/src/race.bash
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+# Copyright 2013 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# race.bash tests the standard library under the race detector.
+# http://golang.org/doc/articles/race_detector.html
+
+set -e
+
+function usage {
+	echo 'race detector is only supported on linux/amd64 and darwin/amd64' 1>&2
+	exit 1
+}
+
+case $(uname) in
+"Darwin")
+	# why Apple? why?
+	if sysctl machdep.cpu.extfeatures | grep -qv EM64T; then
+		usage
+	fi
+	;;
+"Linux")
+	if [ $(uname -m) != "x86_64" ]; then
+		usage
+	fi
+	;;
+*)
+	usage
+	;;
+esac
+
+if [ ! -f make.bash ]; then
+	echo 'race.bash must be run from $GOROOT/src' 1>&2
+	exit 1
+fi
+. ./make.bash --no-banner
+go install -race std
+go test -race -short std
+go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std

このファイルは、実行可能スクリプトとして 100755 のパーミッションで追加されています。

コアとなるコードの解説

src/race.bash スクリプトの各セクションを詳細に解説します。

#!/usr/bin/env bash
# Copyright 2013 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

# race.bash tests the standard library under the race detector.
# http://golang.org/doc/articles/race_detector.html
  • シバン: #!/usr/bin/env bash は、このスクリプトが bash シェルで実行されることを指定します。env を使用することで、bash のパスが環境変数 PATH から解決されるため、システムごとの bash のインストールパスの違いを吸収できます。
  • 著作権表示とライセンス: Goプロジェクトの標準的な著作権表示とBSDスタイルのライセンスに関するコメントです。
  • スクリプトの目的: race.bash tests the standard library under the race detector. というコメントは、このスクリプトの主要な目的が、データ競合検出器を使用してGo標準ライブラリをテストすることであることを明確に示しています。
  • 関連ドキュメントへのリンク: http://golang.org/doc/articles/race_detector.html は、Goのデータ競合検出器に関する公式ドキュメントへのリンクであり、ユーザーが詳細情報を参照できるようにしています。
set -e
  • エラー時の終了: set -e は、スクリプト内で実行されるコマンドが非ゼロの終了ステータスを返した場合(つまりエラーが発生した場合)に、スクリプトの実行を即座に終了させるように設定します。これにより、エラーが発生したにもかかわらずスクリプトが続行し、予期せぬ結果を引き起こすことを防ぎます。
function usage {
	echo 'race detector is only supported on linux/amd64 and darwin/amd64' 1>&2
	exit 1
}
  • usage 関数: この関数は、スクリプトがサポートされていない環境で実行された場合に呼び出されます。
    • echo '...' 1>&2: データ競合検出器が linux/amd64darwin/amd64 でのみサポートされていることを示すエラーメッセージを標準エラー出力(1>&2)に出力します。
    • exit 1: スクリプトを終了し、エラーを示す終了ステータス 1 を返します。
case $(uname) in
"Darwin")
	# why Apple? why?
	if sysctl machdep.cpu.extfeatures | grep -qv EM64T; then
		usage
	fi
	;;
"Linux")
	if [ $(uname -m) != "x86_64" ]; then
		usage
	fi
	;;
*)
	usage
	;;
esac
  • OSとアーキテクチャのチェック: この case ステートメントは、uname コマンドの出力(オペレーティングシステム名)に基づいて、スクリプトがサポートされている環境で実行されているかを確認します。
    • "Darwin" (macOS):
      • sysctl machdep.cpu.extfeatures | grep -qv EM64T: sysctl コマンドでCPUの拡張機能を取得し、grep -qv EM64TEM64T(Intel 64ビットアーキテクチャ)が存在しない場合に真となります。これは、macOSがIntelベースの64ビットCPUで実行されていることを確認するためのものです。もし条件が真であれば、usage 関数が呼び出されます。
    • "Linux":
      • [ $(uname -m) != "x86_64" ]: uname -m コマンドでマシンアーキテクチャを取得し、それが x86_64(64ビット)でない場合に真となります。もし条件が真であれば、usage 関数が呼び出されます。
    • *: 上記のどのケースにも一致しない場合(サポートされていないOSの場合)は、usage 関数が呼び出されます。
if [ ! -f make.bash ]; then
	echo 'race.bash must be run from $GOROOT/src' 1>&2
	exit 1
fi
  • 実行パスの検証: この if ステートメントは、スクリプトがGoのソースツリーの src ディレクトリから実行されていることを確認します。
    • [ ! -f make.bash ]: 現在のディレクトリに make.bash というファイルが存在しない場合に真となります。
    • もし条件が真であれば、race.bash must be run from $GOROOT/src というエラーメッセージを標準エラー出力に出力し、終了ステータス 1 でスクリプトを終了します。これは、make.bash がGoのビルドプロセスにおいて重要な役割を果たすため、その存在が前提となっているためです。
. ./make.bash --no-banner
  • Goビルド環境の初期化: この行は、make.bash スクリプトを現在のシェルでソース(実行)します。
    • . (ドットコマンド): スクリプトを現在のシェル環境で実行することを意味します。これにより、make.bash が設定する環境変数(例: GOROOT)や関数が、race.bash の現在のシェルセッションに引き継がれます。
    • ./make.bash: 現在のディレクトリにある make.bash スクリプトを指定します。
    • --no-banner: make.bash が通常出力するビルド情報のバナーを抑制するオプションです。これにより、スクリプトの出力がより簡潔になります。このステップは、データ競合検出器を有効にしたビルドを行う前に、Goのビルド環境が正しく設定されていることを保証するために不可欠です。
go install -race std
go test -race -short std
go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std
  • データ競合検出器を有効にしたビルドとテスト: これらはスクリプトの主要な実行コマンドです。
    • go install -race std: Goの標準ライブラリ(std)をデータ競合検出器を有効にしてコンパイルし、インストールします。これにより、標準ライブラリのバイナリにデータ競合検出器のランタイムサポートが組み込まれます。
    • go test -race -short std: 標準ライブラリのテストをデータ競合検出器を有効にして実行します。-short フラグは、時間がかかるテストをスキップし、より迅速なテスト実行を可能にします。
    • go test -race -run=nothingplease -bench=.* -benchtime=.1s -cpu=4 std: 標準ライブラリのベンチマークテストをデータ競合検出器を有効にして実行します。
      • -run=nothingplease: 存在しないテスト名を指定することで、通常のテストの実行をスキップし、ベンチマークのみを実行します。
      • -bench=.*: すべてのベンチマークを実行対象とします。
      • -benchtime=.1s: 各ベンチマークの実行時間を0.1秒に制限します。
      • -cpu=4: ベンチマークテストを4つのCPUコアで並行して実行します。

これらのコマンドは、Go標準ライブラリがデータ競合を起こさないことを確認するために、様々なシナリオでデータ競合検出器を適用し、包括的なテストカバレッジを提供します。

関連リンク

参考にした情報源リンク