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

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

このコミットは、Go言語のランタイムにおいて、GOTRACEBACK環境変数にcrashという新しいオプションを追加するものです。これにより、パニック発生時に通常のトレースバック出力に加えて、オペレーティングシステム固有の方法でプログラムをクラッシュさせ、コアダンプを生成できるようになります。これは、特に本番環境でのデバッグにおいて、詳細なメモリ状態を分析するために非常に有用な機能です。

コミット

commit 5146a93e72e870b06150c5419e1b83056ecc697b
Author: Russ Cox <rsc@golang.org>
Date:   Fri Mar 15 01:11:03 2013 -0400

    runtime: accept GOTRACEBACK=crash to mean 'crash after panic'
    
    This provides a way to generate core dumps when people need them.
    The settings are:
    
            GOTRACEBACK=0  no traceback on panic, just exit
            GOTRACEBACK=1  default - traceback on panic, then exit
            GOTRACEBACK=2  traceback including runtime frames on panic, then exit
            GOTRACEBACK=crash traceback including runtime frames on panic, then crash
    
    Fixes #3257.
    
    R=golang-dev, devon.odell, r, daniel.morsing, ality
    CC=golang-dev
    https://golang.org/cl/7666044

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

https://github.com/golang/go/commit/5146a93e72e870b06150c5419e1b83056ecc697b

元コミット内容

Goランタイムにおいて、GOTRACEBACK=crashという設定を受け入れるように変更しました。これは「パニック後にクラッシュする」ことを意味します。

この変更は、ユーザーがコアダンプを必要とする場合に、それを生成する手段を提供します。GOTRACEBACKの設定は以下の通りです。

  • GOTRACEBACK=0: パニック時にトレースバックを出力せず、すぐに終了します。
  • GOTRACEBACK=1: デフォルト設定。パニック時にトレースバックを出力し、その後終了します。
  • GOTRACEBACK=2: パニック時にランタイムフレームを含むトレースバックを出力し、その後終了します。
  • GOTRACEBACK=crash: パニック時にランタイムフレームを含むトレースバックを出力し、その後クラッシュします。

この変更は、Issue #3257 を修正するものです。

変更の背景

Goプログラムがパニック(予期せぬエラー)を起こして終了する際、通常はスタックトレースが出力されます。しかし、このスタックトレースだけでは、複雑な問題やメモリ破損などの根本原因を特定するのが難しい場合があります。特に、本番環境で発生する再現性の低いバグや、デッドロック、メモリリークといった問題のデバッグには、プログラムがクラッシュした時点のメモリ状態を完全に記録した「コアダンプ」が不可欠です。

従来のGOTRACEBACK設定では、トレースバックの冗長性を制御できましたが、プログラムを意図的にクラッシュさせてコアダンプを生成する直接的な手段がありませんでした。開発者は、Goプログラムがパニックした際に、より詳細なデバッグ情報を得るためにコアダンプを生成したいというニーズを抱えていました。

このコミットは、Issue #3257 で議論されたこのニーズに応えるもので、GOTRACEBACK=crashという新しいオプションを導入することで、パニック発生時にOSレベルでのクラッシュ(通常はSIGABRTシグナルを発生させることによる)をトリガーし、コアダンプの生成を可能にすることを目的としています。これにより、Goプログラムのデバッグ能力が大幅に向上し、特に本番環境での問題解決に貢献します。

前提知識の解説

1. Go言語のパニックとリカバリ (Panic and Recover)

Go言語には、プログラムの異常終了を扱うためのpanicrecoverという組み込み関数があります。

  • panic: 実行時エラーやプログラマが意図的に呼び出すことで、現在のゴルーチン(軽量スレッド)の通常の実行フローを中断させます。パニックが発生すると、そのゴルーチンはスタックを巻き戻し(unwind)、遅延関数(deferで登録された関数)が実行されます。もし、recoverによってパニックが捕捉されなければ、プログラム全体が終了します。
  • recover: defer関数内で呼び出されることで、パニックを捕捉し、そのゴルーチンの実行を再開させることができます。これにより、プログラムの異常終了を防ぎ、エラーハンドリングを行うことが可能です。

このコミットは、recoverされずにプログラム全体が終了するパニックに焦点を当てています。

2. スタックトレース (Stack Trace)

プログラムがパニックやエラーで終了する際に、その時点での関数呼び出しの履歴(コールスタック)を表示したものです。これにより、どの関数がどの関数を呼び出し、最終的にどこでエラーが発生したのかを追跡できます。GoのGOTRACEBACK環境変数は、このスタックトレースの表示レベルを制御します。

3. コアダンプ (Core Dump)

プログラムがクラッシュした時点のメモリイメージをファイルに保存したものです。このファイルには、プログラムの実行中に使用されていたすべてのメモリ内容(変数、スタック、ヒープなど)が含まれます。コアダンプは、デバッガ(例: GDB、LLDB)で開くことができ、クラッシュ時のプログラムの状態を詳細に分析するために使用されます。メモリ破損、ポインタの不正アクセス、デッドロックなど、スタックトレースだけでは原因特定が難しい複雑なバグの解析に不可欠です。

4. シグナル (Signals)

Unix系OSにおけるプロセス間通信(IPC)の一種で、ソフトウェア割り込みのようなものです。OSがプロセスに対して特定のイベント(例: 割り込み、エラー、終了要求)を通知するために使用します。

  • SIGABRT (Abort Signal): プロセス自身が異常終了を要求する際に送信されるシグナルです。通常、abort()関数を呼び出すことで発生し、デフォルトではコアダンプを生成してプロセスを終了させます。
  • SIGPIPE (Broken Pipe Signal): パイプやソケットへの書き込み中に、読み込み側がクローズされた場合に発生するシグナルです。デフォルトではプロセスを終了させます。このコミットでは、既存のraisesigpipe関数がより汎用的なraise関数に置き換えられ、SIGABRTを送信するために利用されます。

5. 環境変数 GOTRACEBACK

Goプログラムの実行時に、パニック発生時のトレースバックの挙動を制御する環境変数です。

  • GOTRACEBACK=0: トレースバックを完全に抑制します。
  • GOTRACEBACK=1 (デフォルト): ランタイム内部のフレームを除外したトレースバックを表示します。
  • GOTRACEBACK=2: ランタイム内部のフレームを含む完全なトレースバックを表示します。
  • GOTRACEBACK=crash (このコミットで追加): ランタイム内部のフレームを含む完全なトレースバックを表示した後、OS固有の方法でプログラムをクラッシュさせ、コアダンプを生成します。

技術的詳細

このコミットの主要な技術的変更点は、GOTRACEBACK環境変数にcrashオプションを追加し、Goランタイムがパニック時にOSレベルでのクラッシュをトリガーするメカニズムを導入したことです。

  1. GOTRACEBACK値の解析の拡張:

    • src/pkg/runtime/runtime.c内のruntime·gotraceback関数が変更されました。
    • この関数は、以前はGOTRACEBACKの値(0, 1, 2)を整数として解析していましたが、新たに"crash"という文字列も認識するようになりました。
    • runtime·gotraceback関数は、bool *crashという新しい引数を受け取るようになりました。GOTRACEBACK="crash"が設定されている場合、このcrashポインタが指す値がtrueに設定されます。これにより、呼び出し元はパニック後にクラッシュ処理を行うべきかどうかを判断できます。
  2. runtime·crash関数の導入:

    • runtime·crashという新しい関数が導入されました。この関数は、OS固有の方法でプログラムをクラッシュさせる責任を負います。
    • Unix系システム(Linux, FreeBSD, NetBSD, OpenBSD, Darwin)では、src/pkg/runtime/signal_unix.cruntime·crashが実装されています。この実装では、まずSIGABRTシグナルのハンドラをデフォルトに戻し(runtime·setsig(SIGABRT, SIG_DFL, false))、その後SIGABRTシグナルを自身に送信します(runtime·raise(SIGABRT))。SIGABRTのデフォルトの挙動はコアダンプを生成して終了することです。
    • macOS (Darwin) の64-bitシステムでは、コアダンプが非常に大きくなる(128GB以上)可能性があるため、runtime·crash関数は意図的に何もしないように実装されています。これは、ユーザーが誤って巨大なコアダンプを生成し、システムリソースを消費するのを防ぐための配慮です。
    • Windowsシステムでは、src/pkg/runtime/os_windows.cruntime·crashのスタブが追加されています。コメントには「Goがシグナルをインターセプトしない場合と同様に、Windowsプログラムをアボート/クラッシュさせるために必要なことを行うべき」と記載されており、この時点ではまだ具体的な実装は行われていませんが、将来的な拡張の余地が残されています。
  3. シグナルハンドラとパニック処理の変更:

    • src/pkg/runtime/panic.c内のruntime·dopanic関数や、各OSのシグナルハンドラ(例: src/pkg/runtime/os_plan9_386.csrc/pkg/runtime/signal_386.cなど)が変更されました。
    • これらの関数は、runtime·gotracebackを呼び出す際に、crashフラグを受け取るように修正されました。
    • トレースバックの出力後、もしcrashフラグがtrueであれば、runtime·crash()関数が呼び出され、プログラムが意図的にクラッシュさせられます。
  4. runtime·raisesigpipeからruntime·raiseへの汎用化:

    • 以前はSIGPIPEシグナルを発生させるためのruntime·raisesigpipeという関数がありましたが、これがより汎用的なruntime·raise(int32 sig)関数に置き換えられました。
    • このruntime·raise関数は、引数として任意のシグナル番号を受け取り、そのシグナルを現在のプロセス(またはスレッド)に送信します。これにより、runtime·crash関数がSIGABRTを送信するためにこの汎用関数を利用できるようになりました。
    • 各OS/アーキテクチャ固有のアセンブリファイル(例: src/pkg/runtime/sys_darwin_386.ssrc/pkg/runtime/sys_linux_amd64.sなど)で、runtime·raisesigpipeの実装がruntime·raiseの実装に置き換えられ、引数としてシグナル番号を受け取るように変更されています。

これらの変更により、Goプログラムはパニック時に開発者が望む形でコアダンプを生成できるようになり、デバッグの選択肢が広がりました。

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

このコミットで変更された主要なファイルと、その変更の概要は以下の通りです。

  • src/pkg/runtime/extern.go:

    • GOTRACEBACK環境変数の説明が更新され、crashオプションが追加されました。
  • src/pkg/runtime/runtime.c:

    • runtime·gotraceback関数のシグネチャがint32 runtime·gotraceback(bool *crash)に変更され、crashポインタを介してGOTRACEBACK="crash"が設定されたかどうかを呼び出し元に通知するようになりました。
    • GOTRACEBACK="crash"の場合に*crash = trueを設定し、トレースバックレベルとして2(ランタイムフレームを含む)を返すロジックが追加されました。
  • src/pkg/runtime/runtime.h:

    • runtime·gotraceback関数のプロトタイプが更新され、bool *crash引数が追加されました。
    • runtime·crash関数のプロトタイプが追加されました。
  • src/pkg/runtime/panic.c:

    • runtime·dopanic関数内でruntime·gotracebackを呼び出す際に、crashフラグを受け取るように変更されました。
    • トレースバック出力後、crashフラグがtrueの場合にruntime·crash()を呼び出すロジックが追加されました。
  • src/pkg/runtime/proc.c:

    • runtime·tracebackothers関数内でruntime·gotracebackを呼び出す際に、nilを渡すように変更されました(このコンテキストではクラッシュは不要なため)。
  • src/pkg/runtime/signal_unix.c:

    • runtime·crash関数が実装されました。この関数は、SIGABRTシグナルのハンドラをデフォルトに戻し、runtime·raise(SIGABRT)を呼び出してプロセスをクラッシュさせます。
    • macOS (Darwin) の64-bitシステムでは、巨大なコアダンプを避けるためにruntime·crashが何もしないように実装されています。
    • os·sigpipe関数内でruntime·raisesigpipe()の代わりにruntime·raise(SIGPIPE)が呼び出されるように変更されました。
  • src/pkg/runtime/signal_unix.h:

    • runtime·raisesigpipeのプロトタイプが削除され、runtime·raise(int32)のプロトタイプが追加されました。
  • src/pkg/runtime/os_plan9.c, src/pkg/runtime/os_plan9_386.c, src/pkg/runtime/os_plan9_amd64.c:

    • Plan 9 向けのruntime·crash関数が追加されました。
    • シグナルハンドラ内でruntime·gotracebackの呼び出しとruntime·crashの呼び出しが追加されました。
  • src/pkg/runtime/os_windows.c, src/pkg/runtime/os_windows_386.c, src/pkg/runtime/os_windows_amd64.c:

    • Windows 向けのruntime·crash関数のスタブが追加されました。
    • シグナルハンドラ内でruntime·gotracebackの呼び出しとruntime·crashの呼び出しが追加されました。
  • src/pkg/runtime/signal_386.c, src/pkg/runtime/signal_amd64.c, src/pkg/runtime/signal_arm.c:

    • 各アーキテクチャのシグナルハンドラ内でruntime·gotracebackの呼び出しとruntime·crashの呼び出しが追加されました。
  • src/pkg/runtime/symtab.c:

    • runtime·showframe関数内でruntime·gotracebackを呼び出す際に、nilを渡すように変更されました。
  • src/pkg/runtime/sys_darwin_386.s, src/pkg/runtime/sys_darwin_amd64.s, src/pkg/runtime/sys_freebsd_*.s, src/pkg/runtime/sys_linux_*.s, src/pkg/runtime/sys_netbsd_*.s, src/pkg/runtime/sys_openbsd_*.s:

    • 各OS/アーキテクチャ固有のアセンブリファイルで、runtime·raisesigpipeの実装がruntime·raiseの実装に置き換えられ、引数としてシグナル番号を受け取るように変更されました。

コアとなるコードの解説

runtime·gotraceback関数の変更 (src/pkg/runtime/runtime.c)

// The GOTRACEBACK environment variable controls the
// behavior of a Go program that is crashing and exiting.
//	GOTRACEBACK=0   suppress all tracebacks
//	GOTRACEBACK=1   default behavior - show tracebacks but exclude runtime frames
//	GOTRACEBACK=2   show tracebacks including runtime frames
//	GOTRACEBACK=crash   show tracebacks including runtime frames, then crash (core dump etc)
int32
runtime·gotraceback(bool *crash)
{
 	byte *p;

	if(crash != nil)
		*crash = false;
 	p = runtime·getenv("GOTRACEBACK");
 	if(p == nil || p[0] == '\0')
 		return 1;	// default is on
 	if(runtime·strcmp(p, (byte*)"crash") == 0) {
 		if(crash != nil)
 			*crash = true;
 		return 2;	// extra information
 	}
 	return runtime·atoi(p);
}

この関数は、GOTRACEBACK環境変数の値を読み取り、パニック時のトレースバックの挙動を決定します。 変更点として、bool *crashという新しい引数が追加されました。これは、呼び出し元がパニック後にクラッシュ処理を行うべきかどうかを知るためのフラグです。

  • GOTRACEBACKが設定されていないか空の場合、デフォルトの1(トレースバック表示)を返します。
  • GOTRACEBACK"crash"と設定されている場合、crashポインタがnilでなければ*crashtrueに設定し、トレースバックレベルとして2(ランタイムフレームを含む)を返します。
  • それ以外の場合、環境変数の値を整数として解析して返します。

runtime·crash関数の実装 (src/pkg/runtime/signal_unix.c - Unix系OSの場合)

void
runtime·crash(void)
{
#ifdef GOOS_darwin
	// OS X core dumps are linear dumps of the mapped memory,
	// from the first virtual byte to the last, with zeros in the gaps.
	// Because of the way we arrange the address space on 64-bit systems,
	// this means the OS X core file will be >128 GB and even on a zippy
	// workstation can take OS X well over an hour to write (uninterruptible).
	// Save users from making that mistake.
	if(sizeof(void*) == 8)
		return;
#endif

	runtime·setsig(SIGABRT, SIG_DFL, false);
	runtime·raise(SIGABRT);
}

このruntime·crash関数は、GoプログラムをOSレベルでクラッシュさせるための主要なロジックを含んでいます。

  • macOS (Darwin) 64-bitの特殊処理: GOOS_darwinが定義されており、かつポインタサイズが8バイト(64-bitシステム)の場合、関数はすぐにリターンします。これは、macOSのコアダンプの特性(マッピングされたメモリの線形ダンプであり、64-bitシステムでは非常に巨大なファイルになる)を考慮し、ユーザーが意図せず巨大なコアダンプを生成してしまうのを防ぐためのものです。
  • SIGABRTの送信: それ以外のUnix系OSでは、まずSIGABRTシグナルのハンドラをデフォルト(SIG_DFL)に戻します。これにより、Goランタイムが設定したカスタムシグナルハンドラが解除され、OSがSIGABRTを処理する際のデフォルトの挙動(通常はコアダンプの生成とプロセス終了)が有効になります。
  • runtime·raise(SIGABRT): その後、runtime·raise関数を呼び出して自身にSIGABRTシグナルを送信します。これにより、プログラムはクラッシュし、コアダンプが生成されます。

runtime·raise関数の導入と各OS/アーキテクチャのアセンブリ変更

以前はruntime·raisesigpipeという特定のシグナル(SIGPIPE)を発生させる関数がありましたが、このコミットでruntime·raise(int32 sig)という汎用的な関数に置き換えられました。この関数は、引数として渡されたシグナル番号を現在のプロセス/スレッドに送信します。

例えば、src/pkg/runtime/sys_darwin_amd64.sでは、以下のように変更されています。

// Old: TEXT runtime·raisesigpipe(SB),7,$24
// Old: 	... (SIGPIPEを送信するロジック)

// New:
TEXT runtime·raise(SB),7,$24
	MOVL	$(0x2000000+20), AX // getpid
	SYSCALL
	MOVQ	AX, DI	// arg 1 - pid
	MOVL	sig+0(FP), SI	// arg 2 - signal (引数から取得)
	MOVL	$(0x2000000+37), AX // kill
	SYSCALL
	RET

このアセンブリコードは、macOS (amd64) においてkillシステムコールを使用して指定されたシグナルを送信する処理を示しています。sig+0(FP)は、関数に渡されたsig引数の値(シグナル番号)をレジスタにロードしています。これにより、runtime·crash関数がruntime·raise(SIGABRT)を呼び出すことで、OSがコアダンプを生成するような形でプログラムを終了させることが可能になります。

これらの変更により、GoランタイムはGOTRACEBACK=crash設定に応じて、OSの機能を利用して意図的にコアダンプを生成するメカニズムを確立しました。

関連リンク

参考にした情報源リンク

このコミットは、Go言語のランタイムにおいて、GOTRACEBACK環境変数にcrashという新しいオプションを追加するものです。これにより、パニック発生時に通常のトレースバック出力に加えて、オペレーティングシステム固有の方法でプログラムをクラッシュさせ、コアダンプを生成できるようになります。これは、特に本番環境でのデバッグにおいて、詳細なメモリ状態を分析するために非常に有用な機能です。

コミット

commit 5146a93e72e870b06150c5419e1b83056ecc697b
Author: Russ Cox <rsc@golang.org>
Date:   Fri Mar 15 01:11:03 2013 -0400

    runtime: accept GOTRACEBACK=crash to mean 'crash after panic'
    
    This provides a way to generate core dumps when people need them.
    The settings are:
    
            GOTRACEBACK=0  no traceback on panic, just exit
            GOTRACEBACK=1  default - traceback on panic, then exit
            GOTRACEBACK=2  traceback including runtime frames on panic, then exit
            GOTRACEBACK=crash traceback including runtime frames on panic, then crash
    
    Fixes #3257.
    
    R=golang-dev, devon.odell, r, daniel.morsing, ality
    CC=golang-dev
    https://golang.org/cl/7666044

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

https://github.com/golang/go/commit/5146a93e72e870b06150c5419e1b83056ecc697b

元コミット内容

Goランタイムにおいて、GOTRACEBACK=crashという設定を受け入れるように変更しました。これは「パニック後にクラッシュする」ことを意味します。

この変更は、ユーザーがコアダンプを必要とする場合に、それを生成する手段を提供します。GOTRACEBACKの設定は以下の通りです。

  • GOTRACEBACK=0: パニック時にトレースバックを出力せず、すぐに終了します。
  • GOTRACEBACK=1: デフォルト設定。パニック時にトレースバックを出力し、その後終了します。
  • GOTRACEBACK=2: パニック時にランタイムフレームを含むトレースバックを出力し、その後終了します。
  • GOTRACEBACK=crash: パニック時にランタイムフレームを含むトレースバックを出力し、その後クラッシュします。

この変更は、Issue #3257 を修正するものです。

変更の背景

Goプログラムがパニック(予期せぬエラー)を起こして終了する際、通常はスタックトレースが出力されます。しかし、このスタックトレースだけでは、複雑な問題やメモリ破損などの根本原因を特定するのが難しい場合があります。特に、本番環境で発生する再現性の低いバグや、デッドロック、メモリリークといった問題のデバッグには、プログラムがクラッシュした時点のメモリ状態を完全に記録した「コアダンプ」が不可欠です。

従来のGOTRACEBACK設定では、トレースバックの冗長性を制御できましたが、プログラムを意図的にクラッシュさせてコアダンプを生成する直接的な手段がありませんでした。開発者は、Goプログラムがパニックした際に、より詳細なデバッグ情報を得るためにコアダンプを生成したいというニーズを抱えていました。

このコミットは、Issue #3257 で議論されたこのニーズに応えるもので、GOTRACEBACK=crashという新しいオプションを導入することで、パニック発生時にOSレベルでのクラッシュ(通常はSIGABRTシグナルを発生させることによる)をトリガーし、コアダンプの生成を可能にすることを目的としています。これにより、Goプログラムのデバッグ能力が大幅に向上し、特に本番環境での問題解決に貢献します。

前提知識の解説

1. Go言語のパニックとリカバリ (Panic and Recover)

Go言語には、プログラムの異常終了を扱うためのpanicrecoverという組み込み関数があります。

  • panic: 実行時エラーやプログラマが意図的に呼び出すことで、現在のゴルーチン(軽量スレッド)の通常の実行フローを中断させます。パニックが発生すると、そのゴルーチンはスタックを巻き戻し(unwind)、遅延関数(deferで登録された関数)が実行されます。もし、recoverによってパニックが捕捉されなければ、プログラム全体が終了します。
  • recover: defer関数内で呼び出されることで、パニックを捕捉し、そのゴルーチンの実行を再開させることができます。これにより、プログラムの異常終了を防ぎ、エラーハンドリングを行うことが可能です。

このコミットは、recoverされずにプログラム全体が終了するパニックに焦点を当てています。

2. スタックトレース (Stack Trace)

プログラムがパニックやエラーで終了する際に、その時点での関数呼び出しの履歴(コールスタック)を表示したものです。これにより、どの関数がどの関数を呼び出し、最終的にどこでエラーが発生したのかを追跡できます。GoのGOTRACEBACK環境変数は、このスタックトレースの表示レベルを制御します。

3. コアダンプ (Core Dump)

プログラムがクラッシュした時点のメモリイメージをファイルに保存したものです。このファイルには、プログラムの実行中に使用されていたすべてのメモリ内容(変数、スタック、ヒープなど)が含まれます。コアダンプは、デバッガ(例: GDB、LLDB)で開くことができ、クラッシュ時のプログラムの状態を詳細に分析するために使用されます。メモリ破損、ポインタの不正アクセス、デッドロックなど、スタックトレースだけでは原因特定が難しい複雑なバグの解析に不可欠です。

4. シグナル (Signals)

Unix系OSにおけるプロセス間通信(IPC)の一種で、ソフトウェア割り込みのようなものです。OSがプロセスに対して特定のイベント(例: 割り込み、エラー、終了要求)を通知するために使用します。

  • SIGABRT (Abort Signal): プロセス自身が異常終了を要求する際に送信されるシグナルです。通常、abort()関数を呼び出すことで発生し、デフォルトではコアダンプを生成してプロセスを終了させます。
  • SIGPIPE (Broken Pipe Signal): パイプやソケットへの書き込み中に、読み込み側がクローズされた場合に発生するシグナルです。デフォルトではプロセスを終了させます。このコミットでは、既存のraisesigpipe関数がより汎用的なraise関数に置き換えられ、SIGABRTを送信するために利用されます。

5. 環境変数 GOTRACEBACK

Goプログラムの実行時に、パニック発生時のトレースバックの挙動を制御する環境変数です。

  • GOTRACEBACK=0: トレースバックを完全に抑制します。
  • GOTRACEBACK=1 (デフォルト): ランタイム内部のフレームを除外したトレースバックを表示します。
  • GOTRACEBACK=2: ランタイム内部のフレームを含む完全なトレースバックを表示します。
  • GOTRACEBACK=crash (このコミットで追加): ランタイム内部のフレームを含む完全なトレースバックを表示した後、OS固有の方法でプログラムをクラッシュさせ、コアダンプを生成します。

技術的詳細

このコミットの主要な技術的変更点は、GOTRACEBACK環境変数にcrashオプションを追加し、Goランタイムがパニック時にOSレベルでのクラッシュをトリガーするメカニズムを導入したことです。

  1. GOTRACEBACK値の解析の拡張:

    • src/pkg/runtime/runtime.c内のruntime·gotraceback関数が変更されました。
    • この関数は、以前はGOTRACEBACKの値(0, 1, 2)を整数として解析していましたが、新たに"crash"という文字列も認識するようになりました。
    • runtime·gotraceback関数は、bool *crashという新しい引数を受け取るようになりました。GOTRACEBACK="crash"が設定されている場合、このcrashポインタが指す値がtrueに設定されます。これにより、呼び出し元はパニック後にクラッシュ処理を行うべきかどうかを判断できます。
  2. runtime·crash関数の導入:

    • runtime·crashという新しい関数が導入されました。この関数は、OS固有の方法でプログラムをクラッシュさせる責任を負います。
    • Unix系システム(Linux, FreeBSD, NetBSD, OpenBSD, Darwin)では、src/pkg/runtime/signal_unix.cruntime·crashが実装されています。この実装では、まずSIGABRTシグナルのハンドラをデフォルトに戻し(runtime·setsig(SIGABRT, SIG_DFL, false))、その後SIGABRTシグナルを自身に送信します(runtime·raise(SIGABRT))。SIGABRTのデフォルトの挙動はコアダンプを生成して終了することです。
    • macOS (Darwin) の64-bitシステムでは、コアダンプが非常に大きくなる(128GB以上)可能性があるため、runtime·crash関数は意図的に何もしないように実装されています。これは、ユーザーが誤って巨大なコアダンプを生成し、システムリソースを消費するのを防ぐための配慮です。
    • Windowsシステムでは、src/pkg/runtime/os_windows.cruntime·crashのスタブが追加されています。コメントには「Goがシグナルをインターセプトしない場合と同様に、Windowsプログラムをアボート/クラッシュさせるために必要なことを行うべき」と記載されており、この時点ではまだ具体的な実装は行われていませんが、将来的な拡張の余地が残されています。
  3. シグナルハンドラとパニック処理の変更:

    • src/pkg/runtime/panic.c内のruntime·dopanic関数や、各OSのシグナルハンドラ(例: src/pkg/runtime/os_plan9_386.csrc/pkg/runtime/signal_386.cなど)が変更されました。
    • これらの関数は、runtime·gotracebackを呼び出す際に、crashフラグを受け取るように修正されました。
    • トレースバックの出力後、もしcrashフラグがtrueであれば、runtime·crash()関数が呼び出され、プログラムが意図的にクラッシュさせられます。
  4. runtime·raisesigpipeからruntime·raiseへの汎用化:

    • 以前はSIGPIPEシグナルを発生させるためのruntime·raisesigpipeという関数がありましたが、これがより汎用的なruntime·raise(int32 sig)関数に置き換えられました。
    • このruntime·raise関数は、引数として任意のシグナル番号を受け取り、そのシグナルを現在のプロセス(またはスレッド)に送信します。これにより、runtime·crash関数がSIGABRTを送信するためにこの汎用関数を利用できるようになりました。
    • 各OS/アーキテクチャ固有のアセンブリファイル(例: src/pkg/runtime/sys_darwin_386.ssrc/pkg/runtime/sys_linux_amd64.sなど)で、runtime·raisesigpipeの実装がruntime·raiseの実装に置き換えられ、引数としてシグナル番号を受け取るように変更されています。

これらの変更により、Goプログラムはパニック時に開発者が望む形でコアダンプを生成できるようになり、デバッグの選択肢が広がりました。

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

このコミットで変更された主要なファイルと、その変更の概要は以下の通りです。

  • src/pkg/runtime/extern.go:

    • GOTRACEBACK環境変数の説明が更新され、crashオプションが追加されました。
  • src/pkg/runtime/runtime.c:

    • runtime·gotraceback関数のシグネチャがint32 runtime·gotraceback(bool *crash)に変更され、crashポインタを介してGOTRACEBACK="crash"が設定されたかどうかを呼び出し元に通知するようになりました。
    • GOTRACEBACK="crash"の場合に*crash = trueを設定し、トレースバックレベルとして2(ランタイムフレームを含む)を返すロジックが追加されました。
  • src/pkg/runtime/runtime.h:

    • runtime·gotraceback関数のプロトタイプが更新され、bool *crash引数が追加されました。
    • runtime·crash関数のプロトタイプが追加されました。
  • src/pkg/runtime/panic.c:

    • runtime·dopanic関数内でruntime·gotracebackを呼び出す際に、crashフラグを受け取るように変更されました。
    • トレースバック出力後、crashフラグがtrueの場合にruntime·crash()を呼び出すロジックが追加されました。
  • src/pkg/runtime/proc.c:

    • runtime·tracebackothers関数内でruntime·gotracebackを呼び出す際に、nilを渡すように変更されました(このコンテキストではクラッシュは不要なため)。
  • src/pkg/runtime/signal_unix.c:

    • runtime·crash関数が実装されました。この関数は、SIGABRTシグナルのハンドラをデフォルトに戻し、runtime·raise(SIGABRT)を呼び出してプロセスをクラッシュさせます。
    • macOS (Darwin) の64-bitシステムでは、巨大なコアダンプを避けるためにruntime·crashが何もしないように実装されています。
    • os·sigpipe関数内でruntime·raisesigpipe()の代わりにruntime·raise(SIGPIPE)が呼び出されるように変更されました。
  • src/pkg/runtime/signal_unix.h:

    • runtime·raisesigpipeのプロトタイプが削除され、runtime·raise(int32)のプロトタイプが追加されました。
  • src/pkg/runtime/os_plan9.c, src/pkg/runtime/os_plan9_386.c, src/pkg/runtime/os_plan9_amd64.c:

    • Plan 9 向けのruntime·crash関数が追加されました。
    • シグナルハンドラ内でruntime·gotracebackの呼び出しとruntime·crashの呼び出しが追加されました。
  • src/pkg/runtime/os_windows.c, src/pkg/runtime/os_windows_386.c, src/pkg/runtime/os_windows_amd64.c:

    • Windows 向けのruntime·crash関数のスタブが追加されました。
    • シグナルハンドラ内でruntime·gotracebackの呼び出しとruntime·crashの呼び出しが追加されました。
  • src/pkg/runtime/signal_386.c, src/pkg/runtime/signal_amd64.c, src/pkg/runtime/signal_arm.c:

    • 各アーキテクチャのシグナルハンドラ内でruntime·gotracebackの呼び出しとruntime·crashの呼び出しが追加されました。
  • src/pkg/runtime/symtab.c:

    • runtime·showframe関数内でruntime·gotracebackを呼び出す際に、nilを渡すように変更されました。
  • src/pkg/runtime/sys_darwin_386.s, src/pkg/runtime/sys_darwin_amd64.s, src/pkg/runtime/sys_freebsd_*.s, src/pkg/runtime/sys_linux_*.s, src/pkg/runtime/sys_netbsd_*.s, src/pkg/runtime/sys_openbsd_*.s:

    • 各OS/アーキテクチャ固有のアセンブリファイルで、runtime·raisesigpipeの実装がruntime·raiseの実装に置き換えられ、引数としてシグナル番号を受け取るように変更されました。

コアとなるコードの解説

runtime·gotraceback関数の変更 (src/pkg/runtime/runtime.c)

// The GOTRACEBACK environment variable controls the
// behavior of a Go program that is crashing and exiting.
//	GOTRACEBACK=0   suppress all tracebacks
//	GOTRACEBACK=1   default behavior - show tracebacks but exclude runtime frames
//	GOTRACEBACK=2   show tracebacks including runtime frames
//	GOTRACEBACK=crash   show tracebacks including runtime frames, then crash (core dump etc)
int32
runtime·gotraceback(bool *crash)
{
 	byte *p;

	if(crash != nil)
		*crash = false;
 	p = runtime·getenv("GOTRACEBACK");
 	if(p == nil || p[0] == '\0')
 		return 1;	// default is on
 	if(runtime·strcmp(p, (byte*)"crash") == 0) {
 		if(crash != nil)
 			*crash = true;
 		return 2;	// extra information
 	}
 	return runtime·atoi(p);
}

この関数は、GOTRACEBACK環境変数の値を読み取り、パニック時のトレースバックの挙動を決定します。 変更点として、bool *crashという新しい引数が追加されました。これは、呼び出し元がパニック後にクラッシュ処理を行うべきかどうかを知るためのフラグです。

  • GOTRACEBACKが設定されていないか空の場合、デフォルトの1(トレースバック表示)を返します。
  • GOTRACEBACK"crash"と設定されている場合、crashポインタがnilでなければ*crashtrueに設定し、トレースバックレベルとして2(ランタイムフレームを含む)を返します。
  • それ以外の場合、環境変数の値を整数として解析して返します。

runtime·crash関数の実装 (src/pkg/runtime/signal_unix.c - Unix系OSの場合)

void
runtime·crash(void)
{
#ifdef GOOS_darwin
	// OS X core dumps are linear dumps of the mapped memory,
	// from the first virtual byte to the last, with zeros in the gaps.
	// Because of the way we arrange the address space on 64-bit systems,
	// this means the OS X core file will be >128 GB and even on a zippy
	// workstation can take OS X well over an hour to write (uninterruptible).
	// Save users from making that mistake.
	if(sizeof(void*) == 8)
		return;
#endif

	runtime·setsig(SIGABRT, SIG_DFL, false);
	runtime·raise(SIGABRT);
}

このruntime·crash関数は、GoプログラムをOSレベルでクラッシュさせるための主要なロジックを含んでいます。

  • macOS (Darwin) 64-bitの特殊処理: GOOS_darwinが定義されており、かつポインタサイズが8バイト(64-bitシステム)の場合、関数はすぐにリターンします。これは、macOSのコアダンプの特性(マッピングされたメモリの線形ダンプであり、64-bitシステムでは非常に巨大なファイルになる)を考慮し、ユーザーが意図せず巨大なコアダンプを生成してしまうのを防ぐためのものです。
  • SIGABRTの送信: それ以外のUnix系OSでは、まずSIGABRTシグナルのハンドラをデフォルト(SIG_DFL)に戻します。これにより、Goランタイムが設定したカスタムシグナルハンドラが解除され、OSがSIGABRTを処理する際のデフォルトの挙動(通常はコアダンプの生成とプロセス終了)が有効になります。
  • runtime·raise(SIGABRT): その後、runtime·raise関数を呼び出して自身にSIGABRTシグナルを送信します。これにより、プログラムはクラッシュし、コアダンプが生成されます。

runtime·raise関数の導入と各OS/アーキテクチャのアセンブリ変更

以前はruntime·raisesigpipeという特定のシグナル(SIGPIPE)を発生させる関数がありましたが、このコミットでruntime·raise(int32 sig)という汎用的な関数に置き換えられました。この関数は、引数として渡されたシグナル番号を現在のプロセス/スレッドに送信します。

例えば、src/pkg/runtime/sys_darwin_amd64.sでは、以下のように変更されています。

// Old: TEXT runtime·raisesigpipe(SB),7,$24
// Old: 	... (SIGPIPEを送信するロジック)

// New:
TEXT runtime·raise(SB),7,$24
	MOVL	$(0x2000000+20), AX // getpid
	SYSCALL
	MOVQ	AX, DI	// arg 1 - pid
	MOVL	sig+0(FP), SI	// arg 2 - signal (引数から取得)
	MOVL	$(0x2000000+37), AX // kill
	SYSCALL
	RET

このアセンブリコードは、macOS (amd64) においてkillシステムコールを使用して指定されたシグナルを送信する処理を示しています。sig+0(FP)は、関数に渡されたsig引数の値(シグナル番号)をレジスタにロードしています。これにより、runtime·crash関数がruntime·raise(SIGABRT)を呼び出すことで、OSがコアダンプを生成するような形でプログラムを終了させることが可能になります。

これらの変更により、GoランタイムはGOTRACEBACK=crash設定に応じて、OSの機能を利用して意図的にコアダンプを生成するメカニズムを確立しました。

関連リンク

参考にした情報源リンク