[インデックス 19031] ファイルの概要
このコミットは、Goランタイムにおけるシグナルハンドリングの挙動を改善し、特にランタイム実行中に発生する予期せぬフォールト(メモリ不正アクセスなど)に対する診断情報を強化することを目的としています。具体的には、runtime.canpanic
によるパニック可否のチェックと、それに続くruntime.throw
の呼び出しを、シグナルハンドラからruntime.sigpanic
関数へと移動させています。これにより、エラー発生時にシグナルやコード値、フォールトアドレスといった、より詳細なデバッグ情報が出力されるようになります。
コミット
commit 4110271501f901f53d987fe3a0a0f832b883c8b4
Author: Russ Cox <rsc@golang.org>
Date: Thu Apr 3 19:05:59 2014 -0400
runtime: handle fault during runtime more like unexpected fault address
Delaying the runtime.throw until here will print more information.
In particular it will print the signal and code values, which means
it will show the fault address.
The canpanic checks were added recently, in CL 75320043.
They were just not added in exactly the right place.
LGTM=iant
R=dvyukov, iant
CC=golang-codereviews
https://golang.org/cl/83980043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4110271501f901f53d987fe3a0a0f832b883c8b4
元コミット内容
runtime: handle fault during runtime more like unexpected fault address
Delaying the runtime.throw until here will print more information.
In particular it will print the signal and code values, which means
it will show the fault address.
The canpanic checks were added recently, in CL 75320043.
They were just not added in exactly the right place.
LGTM=iant
R=dvyukov, iant
CC=golang-codereviews
https://golang.org/cl/83980043
変更の背景
このコミットの背景には、Goランタイムが予期せぬシグナル(例えば、無効なメモリアドレスへのアクセスによるセグメンテーション違反など)を受け取った際の診断情報の不足がありました。コミットメッセージによると、runtime.canpanic
によるパニック可否のチェックは最近導入されたものの、その配置が適切ではなかったとされています。
以前の実装では、シグナルが最初に処理されるruntime.sighandler
の段階でcanpanic
チェックが行われ、条件によってはすぐにruntime.throw
が呼び出されていました。しかし、この段階ではシグナルに関する詳細な情報(シグナル番号、シグナルコード、フォールトアドレスなど)が十分に収集されていないか、またはruntime.throw
に渡すことが難しい状況でした。
このコミットは、runtime.throw
の呼び出しをruntime.sigpanic
関数まで遅延させることで、この問題を解決しようとしています。runtime.sigpanic
は、シグナルに関するより豊富なコンテキスト情報にアクセスできるため、そこでruntime.throw
が実行されることで、エラーメッセージにこれらの詳細情報を含めることが可能になります。これにより、デバッグ時の原因特定が大幅に容易になります。
前提知識の解説
-
Goランタイム (Go Runtime): Goプログラムの実行を管理する低レベルなシステムです。ガベージコレクション、ゴルーチンのスケジューリング、チャネル通信、そしてOSからのシグナルハンドリングなど、Go言語の並行処理モデルとメモリ管理を支える重要な役割を担っています。C言語で書かれた部分が多く、OSとのインタフェースを担当します。
-
シグナルハンドリング (Signal Handling): オペレーティングシステム(OS)は、プログラムに対して特定のイベントが発生したことを「シグナル」という形で通知します。例えば、無効なメモリアドレスにアクセスしようとした場合には
SIGSEGV
(セグメンテーション違反)、バスエラーが発生した場合にはSIGBUS
といったシグナルが送られます。Goランタイムはこれらのシグナルを受け取り、適切に処理するメカニズムを持っています。 -
panic
とthrow
:panic
: Go言語におけるプログラムの異常終了メカニズムです。通常、回復不能なエラーが発生した場合にpanic
が起こり、現在のゴルーチンの実行が停止し、遅延関数(defer
)が実行され、最終的にプログラムが終了します。runtime.throw
: Goランタイム内部で、致命的なエラーが発生した際に呼び出される関数です。これはGo言語のpanic
とは異なり、回復不可能なランタイムエラーを示すもので、通常はプログラムを即座に終了させます。runtime.throw
が呼び出されると、スタックトレースなどの診断情報が出力されます。
-
runtime.sigpanic
: Goランタイム内で、OSからシグナルを受け取った際に、そのシグナルをGoのパニックとして処理するために呼び出される関数です。この関数は、受け取ったシグナルの種類に応じて、適切なパニックメッセージを生成し、panic
処理を開始します。 -
runtime.sighandler
: OSからのシグナルを最初に受け取る、低レベルなシグナルハンドラです。この関数は、シグナル発生時のCPUレジスタの状態やシグナルに関する基本的な情報にアクセスできます。 -
runtime.canpanic(g)
: 現在のゴルーチンg
がパニックを発生させても安全な状態であるかどうかをチェックするGoランタイム内部の関数です。例えば、ランタイムが非常にクリティカルな内部処理を行っている最中であったり、既にパニック処理中である場合には、さらなるパニックの発生を抑制するためにfalse
を返すことがあります。これにより、ランタイムの整合性を保ち、無限ループやデッドロックを防ぎます。 -
g
(goroutine): Go言語の軽量スレッドです。Goプログラムの並行処理の基本単位であり、Goランタイムによってスケジューリングされます。 -
m
(machine): OSスレッドに相当するGoランタイムの内部構造です。Goのゴルーチンはm
上で実行されます。
技術的詳細
Goランタイムは、プログラムの実行中に発生する様々なOSシグナルを処理します。特に、SIGSEGV
やSIGBUS
のようなメモリ関連のフォールトシグナルは、プログラムの異常終了に直結するため、その診断情報は非常に重要です。
このコミット以前は、シグナルがruntime.sighandler
によって捕捉された後、一部のケースではruntime.canpanic(g)
のチェックが行われ、もしパニックが許可されない状況であれば、すぐにruntime.throw
が呼び出されていました。この早期のthrow
呼び出しの問題点は、runtime.sighandler
のコンテキストでは、シグナルに関する詳細な情報(例えば、どのメモリアドレスでフォールトが発生したかを示すsigcode1
の値など)がまだ十分に解析されていないか、runtime.throw
に渡す準備ができていない場合があったことです。結果として、出力されるエラーメッセージは「予期せぬシグナル」といった一般的なものに留まり、具体的な原因特定が困難でした。
このコミットの核心的な変更は、runtime.canpanic(g)
のチェックと、それに続くruntime.throw
の呼び出しを、runtime.sighandler
からruntime.sigpanic
関数へと移動させた点にあります。
runtime.sigpanic
関数は、シグナルがGoのパニックとして処理される直前に呼び出されるため、シグナルに関するより豊富な情報(g->sig
、g->sigcode0
、g->sigcode1
など)が既にg
(現在のゴルーチン)のコンテキストに格納されています。このため、runtime.sigpanic
内でruntime.throw
が呼び出されることで、これらの詳細なシグナル情報がエラーメッセージに自動的に含まれるようになります。
特に、g->sigcode1
は多くの場合、フォールトが発生したメモリアドレスを示します。この情報がエラー出力に含まれることで、開発者はどのメモリアドレスへのアクセスが問題を引き起こしたのかを直接確認できるようになり、デバッグの効率が大幅に向上します。
要するに、この変更は、シグナル発生時のエラー報告をより詳細かつ診断に役立つものにすることで、Goプログラムの安定性とデバッグ性を高めるものです。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルが変更されています。
-
src/pkg/runtime/os_darwin.c
、src/pkg/runtime/os_dragonfly.c
、src/pkg/runtime/os_freebsd.c
、src/pkg/runtime/os_linux.c
、src/pkg/runtime/os_nacl.c
、src/pkg/runtime/os_netbsd.c
、src/pkg/runtime/os_openbsd.c
、src/pkg/runtime/os_plan9.c
、src/pkg/runtime/os_solaris.c
、src/pkg/runtime/os_windows.c
:- これらの各OS固有の
runtime·sigpanic
関数の冒頭に、以下のコードが追加されました。if(!runtime·canpanic(g)) runtime·throw("unexpected signal during runtime execution");
- これらの各OS固有の
-
src/pkg/runtime/os_plan9_386.c
、src/pkg/runtime/os_plan9_amd64.c
:- これらのファイル内の
runtime·sighandler
関数から、以下のif
文とgoto Throw;
の行が削除されました。if(flags & SigPanic) { if(!runtime·canpanic(gp)) goto Throw;
- これらのファイル内の
-
src/pkg/runtime/signal_386.c
、src/pkg/runtime/signal_amd64x.c
、src/pkg/runtime/signal_arm.c
:- これらのファイル内の
runtime·sighandler
関数から、以下のif
文とgoto Throw;
の行、およびThrow:
ラベルが削除されました。if(SIG_CODE0(info, ctxt) != SI_USER && (t->flags & SigPanic)) { if(!runtime·canpanic(gp)) goto Throw;
Throw:
- これらのファイル内の
コアとなるコードの解説
このコミットの主要な変更は、runtime.canpanic
チェックとruntime.throw
呼び出しのタイミングと場所を調整することにあります。
-
runtime·sigpanic
への追加: 各OSのsrc/pkg/runtime/os_*.c
ファイルにあるruntime·sigpanic
関数の冒頭に、if(!runtime·canpanic(g)) runtime·throw("unexpected signal during runtime execution");
という行が追加されました。 これは、OSからシグナルを受け取り、Goランタイムがそれをパニックとして処理しようとする際に、まず現在のゴルーチンg
がパニック可能な状態であるかをチェックします。もしruntime.canpanic(g)
がfalse
を返した場合(例えば、ランタイムが非常にデリケートな内部処理を行っている最中など)、それは「予期せぬシグナル」として即座にruntime.throw
を呼び出し、プログラムを終了させます。 この変更の重要な点は、runtime.sigpanic
が呼び出される時点では、シグナルに関する詳細な情報(シグナル番号、シグナルコード、フォールトアドレスなど)が既にg
のコンテキストに格納されているため、runtime.throw
がこれらの情報を含んだ、より診断に役立つエラーメッセージを出力できるようになることです。 -
runtime·sighandler
からの削除:src/pkg/runtime/os_plan9_*.c
およびsrc/pkg/runtime/signal_*.c
ファイル内のruntime·sighandler
関数から、以前存在したif(!runtime·canpanic(gp)) goto Throw;
というチェックと、関連するThrow:
ラベルが削除されました。runtime·sighandler
はOSからのシグナルを最初に受け取る低レベルなハンドラです。以前はここでcanpanic
チェックを行い、パニックが許可されない場合にgoto Throw
でruntime.throw
にジャンプしていました。しかし、この段階ではシグナルに関する情報が十分に集まっていないため、runtime.throw
が呼ばれても詳細な診断情報が得られませんでした。 この変更により、runtime·sighandler
はシグナルをruntime·sigpanic
に適切に引き渡す役割に専念し、runtime·sigpanic
で統一的に、より豊富なコンテキスト情報と共にエラー処理が行われるようになります。これにより、シグナルハンドリングのロジックが整理され、エラー報告の質が向上します。
関連リンク
- Go CL 83980043: https://golang.org/cl/83980043
参考にした情報源リンク
- 特になし(コミットメッセージとコード変更のみに基づいています)