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

[インデックス 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ランタイムはこれらのシグナルを受け取り、適切に処理するメカニズムを持っています。

  • panicthrow:

    • 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シグナルを処理します。特に、SIGSEGVSIGBUSのようなメモリ関連のフォールトシグナルは、プログラムの異常終了に直結するため、その診断情報は非常に重要です。

このコミット以前は、シグナルが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->sigg->sigcode0g->sigcode1など)が既にg(現在のゴルーチン)のコンテキストに格納されています。このため、runtime.sigpanic内でruntime.throwが呼び出されることで、これらの詳細なシグナル情報がエラーメッセージに自動的に含まれるようになります。

特に、g->sigcode1は多くの場合、フォールトが発生したメモリアドレスを示します。この情報がエラー出力に含まれることで、開発者はどのメモリアドレスへのアクセスが問題を引き起こしたのかを直接確認できるようになり、デバッグの効率が大幅に向上します。

要するに、この変更は、シグナル発生時のエラー報告をより詳細かつ診断に役立つものにすることで、Goプログラムの安定性とデバッグ性を高めるものです。

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

このコミットでは、主に以下のファイルが変更されています。

  1. src/pkg/runtime/os_darwin.csrc/pkg/runtime/os_dragonfly.csrc/pkg/runtime/os_freebsd.csrc/pkg/runtime/os_linux.csrc/pkg/runtime/os_nacl.csrc/pkg/runtime/os_netbsd.csrc/pkg/runtime/os_openbsd.csrc/pkg/runtime/os_plan9.csrc/pkg/runtime/os_solaris.csrc/pkg/runtime/os_windows.c:

    • これらの各OS固有のruntime·sigpanic関数の冒頭に、以下のコードが追加されました。
      	if(!runtime·canpanic(g))
      		runtime·throw("unexpected signal during runtime execution");
      
  2. src/pkg/runtime/os_plan9_386.csrc/pkg/runtime/os_plan9_amd64.c:

    • これらのファイル内のruntime·sighandler関数から、以下のif文とgoto Throw;の行が削除されました。
      	if(flags & SigPanic) {
      		if(!runtime·canpanic(gp))
      			goto Throw;
      
  3. src/pkg/runtime/signal_386.csrc/pkg/runtime/signal_amd64x.csrc/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呼び出しのタイミングと場所を調整することにあります。

  1. 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がこれらの情報を含んだ、より診断に役立つエラーメッセージを出力できるようになることです。

  2. 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 Throwruntime.throwにジャンプしていました。しかし、この段階ではシグナルに関する情報が十分に集まっていないため、runtime.throwが呼ばれても詳細な診断情報が得られませんでした。 この変更により、runtime·sighandlerはシグナルをruntime·sigpanicに適切に引き渡す役割に専念し、runtime·sigpanicで統一的に、より豊富なコンテキスト情報と共にエラー処理が行われるようになります。これにより、シグナルハンドリングのロジックが整理され、エラー報告の質が向上します。

関連リンク

参考にした情報源リンク

  • 特になし(コミットメッセージとコード変更のみに基づいています)