[インデックス 18675] ファイルの概要
コミット
commit 0b0e209ffb937f283cdca8b51efadc78cc93f8a1
Author: Joel Sing <jsing@google.com>
Date: Fri Feb 28 03:26:26 2014 +1100
runtime: disable TestSetPanicOnFault for dragonfly/386
This test currently deadlocks on dragonfly/386.
Update #7421
LGTM=minux.ma
R=golang-codereviews, minux.ma
CC=golang-codereviews
https://golang.org/cl/69380043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0b0e209ffb937f283cdca8b51efadc78cc93f8a1
元コミット内容
このコミットは、runtime
パッケージ内のTestSetPanicOnFault
テストをdragonfly/386
アーキテクチャで無効化するものです。コミットメッセージによると、このテストはdragonfly/386
上でデッドロックを引き起こすため、一時的にスキップされることになりました。これはIssue #7421に関連する修正です。
変更の背景
この変更の背景には、Go言語のランタイムテストスイートが特定のオペレーティングシステム(DragonFly BSD)とアーキテクチャ(386、すなわち32ビットx86)の組み合わせで予期せぬ挙動を示したという問題があります。具体的には、TestSetPanicOnFault
というテストがdragonfly/386
環境で実行されると、システムが応答しなくなるデッドロック状態に陥っていました。
Go言語はクロスプラットフォーム対応を重視しており、様々なOSとアーキテクチャで安定して動作することが求められます。そのため、特定の環境でテストが失敗したり、システムを不安定にしたりする問題は、Goの品質と信頼性を維持する上で看過できません。このデッドロックは、GoランタイムがOSのシグナルハンドリング機構とどのように相互作用するか、特にフォールト(メモリ不正アクセスなど)が発生した際の挙動に関連していると考えられます。
Issue #7421は、このデッドロック問題が報告されたGoのバグトラッカーのエントリです。このコミットは、根本的な原因が特定され修正されるまでの間、テストスイートのCI/CDパイプラインがdragonfly/386
環境でブロックされないようにするための暫定的な措置として導入されました。テストをスキップすることで、他の環境での開発やテストの進行を妨げずに、問題の調査と修正に時間を割くことが可能になります。
前提知識の解説
Go言語のランタイム (runtime)
Go言語のプログラムは、Goランタイムと呼ばれる軽量な実行環境上で動作します。このランタイムは、ガベージコレクション、ゴルーチン(軽量スレッド)のスケジューリング、チャネル通信、メモリ管理、システムコールインターフェースなど、Goプログラムの実行に必要な低レベルな機能を提供します。runtime
パッケージは、これらのランタイム機能にアクセスするためのGo言語のインターフェースを提供します。
debug.SetPanicOnFault
debug
パッケージは、デバッグやプロファイリングに関連する機能を提供します。debug.SetPanicOnFault(true)
は、Goプログラムがメモリ不正アクセスなどの「フォールト」を検出した際に、通常のクラッシュではなくGoのパニック(panic)を引き起こすように設定する関数です。これにより、開発者はフォールト発生時にGoのリカバリ機構を利用して、より詳細な情報を取得したり、クリーンアップ処理を実行したりすることが可能になります。これは、C/C++におけるセグメンテーションフォールトのような低レベルなエラーを、Goの例外処理メカニズムに統合する試みと考えることができます。
シグナルハンドリング (Signal Handling)
Unix系OS(DragonFly BSDを含む)では、シグナルはプロセス間通信や、カーネルがプロセスにイベントを通知するためのメカニズムです。メモリ不正アクセス(セグメンテーションフォールト)のようなハードウェア例外は、OSによってSIGSEGV
のようなシグナルとしてプロセスに通知されます。プロセスはこれらのシグナルを捕捉し、カスタムのシグナルハンドラを実行することができます。Goランタイムは、プログラムの安定性とデバッグ可能性を向上させるために、これらのシグナルを捕捉し、適切に処理する独自のシグナルハンドラを設定しています。
シグナルトランポリン (Signal Trampoline)
シグナルトランポリンは、OSがシグナルハンドラを呼び出す際に使用する低レベルなコードの断片です。シグナルが発生すると、カーネルはプロセスの実行コンテキストを保存し、シグナルトランポリンに制御を移します。トランポリンは、実際のシグナルハンドラ関数を呼び出す前に、スタックフレームの設定やレジスタの保存など、必要な準備を行います。シグナルハンドラが終了すると、トランポリンは保存されたコンテキストを復元し、シグナルが発生した場所からプロセスの実行を再開させます。このメカニズムは非常に低レベルであり、OSとアーキテクチャに強く依存します。
DragonFly BSD / 386
- DragonFly BSD: FreeBSDからフォークしたオープンソースのUnix系オペレーティングシステムです。高性能でスケーラブルなシステムを目指しており、特にSMP(Symmetric Multi-Processing)環境でのパフォーマンス向上に注力しています。独自のファイルシステムであるHAMMERファイルシステムなどが特徴です。
- 386 (i386): Intel 80386プロセッサに由来する、32ビットx86アーキテクチャの通称です。現代のほとんどのデスクトップやサーバーは64ビットアーキテクチャ(x86-64またはamd64)ですが、組み込みシステムや古いハードウェアでは32ビットアーキテクチャがまだ使用されることがあります。Go言語は、様々なアーキテクチャをサポートしており、32ビットx86もその一つです。
この特定の組み合わせ(dragonfly/386
)で問題が発生したということは、Goランタイムのシグナルハンドリングコードが、DragonFly BSDの32ビットx86環境におけるシグナルトランポリンの挙動と完全に互換性がなかったことを示唆しています。
技術的詳細
このコミットは、Goランタイムのテストスイートにおける特定のテストTestSetPanicOnFault
が、dragonfly/386
環境でデッドロックを引き起こす問題に対処するためのものです。
Goランタイムは、プログラムが不正なメモリアクセスなどのフォールトを発生させた際に、OSからのシグナル(例: SIGSEGV
)を捕捉し、それをGoのパニックに変換するメカニズムを持っています。これはdebug.SetPanicOnFault(true)
によって有効化されます。この機能は、低レベルなクラッシュをGoの例外処理フローに統合することで、より堅牢なエラーハンドリングやデバッグを可能にすることを目的としています。
問題は、dragonfly/386
環境において、このシグナルハンドリングのプロセスが「シグナルトランポリンでのフォールト」を引き起こし、結果としてデッドロックに至るという点にありました。シグナルトランポリンは、OSがシグナルハンドラを呼び出す際に使用する非常に低レベルなアセンブリコードの断片です。シグナルが発生すると、OSはプロセスの実行コンテキストを保存し、シグナルトランポリンにジャンプします。トランポリンは、Goランタイムが設定した実際のシグナルハンドラを呼び出す前に、スタックの調整やレジスタの保存など、必要な準備を行います。
dragonfly/386
でのデッドロックは、おそらく以下のいずれかのシナリオで発生したと考えられます。
- シグナルトランポリン内のフォールト:
debug.SetPanicOnFault(true)
が有効な状態でフォールトが発生し、OSがシグナルトランポリンを介してGoのシグナルハンドラを呼び出そうとした際に、そのトランポリン自体が何らかの理由で不正なメモリアクセスなどのフォールトを発生させた。これにより、Goランタイムが設定したシグナルハンドラが正常に起動せず、システムが不安定な状態に陥った。 - シグナルハンドラの再入可能性問題: シグナルハンドラが実行中に、別のシグナル(または同じシグナル)が再帰的に発生し、シグナルハンドラが再入可能でないためにデッドロックを引き起こした。特に、
debug.SetPanicOnFault
が有効な場合、フォールトがパニックに変換される過程で、さらなる低レベルな操作が行われる可能性があり、これが特定のOS/アーキテクチャの組み合わせで競合状態やデッドロックを引き起こすことがあります。 - スタックの破損または不整合: シグナルトランポリンやGoのシグナルハンドラがスタックを操作する際に、
dragonfly/386
の特定のABI(Application Binary Interface)やスタックフレームの規約と合致せず、スタックが破損したり不整合が生じたりして、その後の実行が不可能になった。
このコミットは、根本的な原因を修正するのではなく、問題のあるテストを特定の環境でスキップすることで、CI/CDパイプラインのブロックを解除し、他の環境での開発を継続できるようにする暫定的な解決策です。これは、Goのテストスイートが様々なプラットフォームで実行されることを保証するための一般的なプラクティスです。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/runtime_test.go
+++ b/src/pkg/runtime/runtime_test.go
@@ -135,6 +135,12 @@ func TestStopCPUProfilingWithProfilerOff(t *testing.T) {
}
func TestSetPanicOnFault(t *testing.T) {
+ // This currently results in a fault in the signal trampoline on
+ // dragonfly/386 - see issue 7421.
+ if GOOS == "dragonfly" && GOARCH == "386" {
+ t.Skip("skipping test on dragonfly/386")
+ }
+
old := debug.SetPanicOnFault(true)
defer debug.SetPanicOnFault(old)
コアとなるコードの解説
変更はsrc/pkg/runtime/runtime_test.go
ファイルのTestSetPanicOnFault
関数内で行われています。
追加されたコードは以下の通りです。
// This currently results in a fault in the signal trampoline on
// dragonfly/386 - see issue 7421.
if GOOS == "dragonfly" && GOARCH == "386" {
t.Skip("skipping test on dragonfly/386")
}
-
コメント:
// This currently results in a fault in the signal trampoline on
// dragonfly/386 - see issue 7421.
このコメントは、なぜこの変更が行われたかを明確に説明しています。dragonfly/386
環境でこのテストを実行すると、シグナルトランポリン内でフォールトが発生し、Issue #7421で報告されている問題につながることを示しています。これは、Goのテストコードにおける良いプラクティスであり、将来のメンテナがこのスキップの理由を理解するのに役立ちます。 -
条件分岐:
if GOOS == "dragonfly" && GOARCH == "386" { ... }
Goのビルドシステムは、コンパイル時にGOOS
(オペレーティングシステム)とGOARCH
(アーキテクチャ)という環境変数を提供します。このif
文は、現在のテストがdragonfly
オペレーティングシステムと386
アーキテクチャの組み合わせで実行されているかどうかをチェックします。 -
テストのスキップ:
t.Skip("skipping test on dragonfly/386")
testing
パッケージのt.Skip()
関数は、現在のテストをスキップするために使用されます。引数として渡された文字列は、テスト結果の出力に表示され、なぜテストがスキップされたのかを示します。この場合、「dragonfly/386
上でテストをスキップしています」というメッセージが表示されます。
この変更により、TestSetPanicOnFault
テストは、dragonfly/386
環境で実行された場合に即座にスキップされ、デッドロックの発生を防ぎます。これは、問題の根本的な解決ではなく、テストスイートの安定性を確保するための回避策です。根本的な原因の調査と修正は、Issue #7421で追跡されることになります。
関連リンク
- Go Issue 7421: https://github.com/golang/go/issues/7421 (このコミットが参照しているGoのバグトラッカーのIssue)
- Go CL 69380043: https://golang.org/cl/69380043 (このコミットに対応するGerritの変更リスト)
参考にした情報源リンク
- Go言語公式ドキュメント (runtimeパッケージ): https://pkg.go.dev/runtime
- Go言語公式ドキュメント (debugパッケージ): https://pkg.go.dev/runtime/debug
- Go言語公式ドキュメント (testingパッケージ): https://pkg.go.dev/testing
- DragonFly BSD 公式サイト: https://www.dragonflybsd.org/
- x86アーキテクチャ (Wikipedia): https://ja.wikipedia.org/wiki/X86%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3
- シグナル (Unix) (Wikipedia): https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B0%E3%83%8A%E3%83%AB_(Unix)
- Application Binary Interface (ABI) (Wikipedia): https://ja.wikipedia.org/wiki/Application_Binary_Interface