[インデックス 17556] ファイルの概要
このコミットは、Goランタイムのスタックトレース表示に関する改善です。具体的には、パニック発生時にruntime.panic
フレームがスタックトレースに常に表示されるように変更することで、デバッグ時の情報量を増やし、パニックの発生源をより明確に理解できるようにすることを目的としています。
コミット
commit fa4984d535b23c0d2b14650a8842d63083893af3
Author: Russ Cox <rsc@golang.org>
Date: Wed Sep 11 11:59:19 2013 -0400
runtime: show runtime.panic frame in traceback
Otherwise, if panic starts running deferred functions,
the code that panicked appears to be calling those
functions directly, which is not the case and can be
confusing.
For example:
main.Two()
/Users/rsc/x.go:12 +0x2a
runtime.panic(0x20dc0, 0x2100cc010)
/Users/rsc/g/go/src/pkg/runtime/panic.c:248 +0x106
main.One()
/Users/rsc/x.go:8 +0x55
This makes clear(er) that main.Two is being called during
a panic, not as a direct call from main.One.
Fixes #5832.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13302051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fa4984d535b23c0d2b14650a8842d63083893af3
元コミット内容
このコミットの元の内容は、Goランタイムが生成するスタックトレースにおいて、runtime.panic
関数呼び出しのフレームを常に表示するように変更することです。これにより、パニックが発生し、遅延関数(deferred functions)が実行される際に、スタックトレースが誤解を招くような表示になる問題を解決します。
具体的には、パニックが遅延関数を実行し始めると、パニックを引き起こしたコードが、あたかもそれらの遅延関数を直接呼び出しているかのように見えてしまうことがありました。これは実際にはそうではないため、混乱を招く可能性がありました。
コミットメッセージの例では、main.Two()
がパニックを引き起こし、その後にruntime.panic
が呼ばれ、さらにmain.One()
がスタックトレースに現れるケースが示されています。この変更により、runtime.panic
フレームが明示的に表示されることで、main.Two
がパニック中に呼び出されているのであって、main.One
からの直接の呼び出しではないことがより明確になります。
この変更は、GoのIssue #5832を修正するものです。
変更の背景
Go言語では、プログラムの実行中に予期せぬエラーが発生した場合、panic
メカニズムが使用されます。panic
が発生すると、通常のプログラムフローは中断され、現在のゴルーチン(goroutine)の遅延関数が実行され始めます。その後、スタックが巻き戻され(unwind)、最終的にプログラムがクラッシュするか、recover
関数によってパニックが捕捉されない限り、プログラムは終了します。
このパニック処理の過程で、スタックトレースが生成され、開発者は問題の原因を特定するための重要な情報を得ることができます。しかし、このコミットが導入される以前は、runtime.panic
関数自体がスタックトレースから省略されることがありました。
この省略は、特に遅延関数が複雑な処理を行う場合や、複数の関数呼び出しが絡む場合に、スタックトレースの解釈を困難にしていました。パニックを引き起こした真の関数が、あたかも遅延関数を直接呼び出しているかのように見えてしまうため、デバッグ時に誤った推論をしてしまう可能性があったのです。
Issue #5832では、この問題が具体的に報告されており、runtime.panic
フレームの欠落がスタックトレースの可読性を損ね、デバッグ体験を悪化させていることが指摘されていました。このコミットは、このデバッグ上の課題を解決し、より正確で理解しやすいスタックトレースを提供することを目的としています。
前提知識の解説
Go言語のパニックとリカバリー (Panic and Recover)
- Panic: Go言語におけるランタイムエラーの一種で、プログラムの異常終了を引き起こします。
panic
関数を明示的に呼び出すこともできますし、ゼロポインタ参照や配列の範囲外アクセスなどのランタイムエラーによって暗黙的に発生することもあります。panic
が発生すると、現在の関数の実行は即座に停止し、その関数に登録されているdefer
関数が実行されます。その後、呼び出し元の関数へとスタックが巻き戻され、各関数のdefer
関数が順に実行されます。このプロセスがゴルーチンのスタックの最上位まで続くと、プログラムはクラッシュします。 - Defer:
defer
文は、その関数がリターンする直前(またはパニックによってスタックが巻き戻される直前)に実行される関数を登録するために使用されます。リソースの解放(ファイルのクローズ、ロックの解除など)や、パニックからの回復(recover
の使用)によく利用されます。 - Recover:
recover
関数は、defer
関数内で呼び出された場合にのみ有効です。recover
が呼び出されると、現在のゴルーチンで発生しているパニックを捕捉し、パニックの値を返します。これにより、パニックによるプログラムのクラッシュを防ぎ、通常の実行フローを再開させることができます。recover
がdefer
関数以外で呼び出された場合、またはパニックが発生していないときに呼び出された場合、nil
を返します。
スタックトレース (Stack Trace)
スタックトレースは、プログラムの実行中に特定の時点(通常はエラーやパニックが発生した時点)で、現在実行中の関数とその呼び出し元の関数がどのように連鎖しているかを示すリストです。各エントリ(フレーム)は、関数名、ファイル名、行番号、およびプログラムカウンタ(PC)オフセットなどの情報を含みます。スタックトレースは、問題の根本原因を特定し、プログラムの実行パスを理解するために不可欠なデバッグツールです。
Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理するシステムです。これには、ガベージコレクタ、スケジューラ(ゴルーチンの管理)、メモリ管理、プリミティブな同期メカニズム、そしてパニック処理などが含まれます。Goプログラムがコンパイルされると、ランタイムのコードが実行可能バイナリにリンクされます。このコミットで変更されているsrc/pkg/runtime/symtab.c
は、Goランタイムの一部であり、スタックトレースのシンボル解決や表示ロジックに関わるC言語のソースファイルです。
m->throwing
(Goランタイム内部変数)
Goランタイムの内部では、m
はM(Machine)構造体を指し、OSのスレッドを表します。m->throwing
は、このMが現在パニック処理中であるかどうかを示すフラグです。このコミットでは、m->throwing
が単なる真偽値ではなく、パニックの深度を示すカウンタとして扱われるようになったため、m->throwing > 0
というチェックに変更されています。これは、複数のパニックが同時に発生するような稀なケース(例えば、defer
関数内でさらにパニックが発生するような場合)に対応するため、または単に内部的な表現の変更によるものです。
技術的詳細
このコミットの技術的な核心は、Goランタイムのスタックトレース生成ロジック、特にruntime·showframe
関数の変更にあります。
runtime·showframe
関数は、スタックトレースを生成する際に、特定のフレームを表示するかどうかを決定する役割を担っています。Goのスタックトレースは、デバッグの関連性を高めるために、一部の内部ランタイム関数フレームを省略することがあります。しかし、runtime.panic
フレームの省略は、パニックの発生源を曖昧にするという副作用がありました。
変更前は、m->throwing
が真(パニック処理中)の場合、特定のフレーム(おそらくはパニック処理に関連する内部フレーム)を表示しないロジックがありました。このコミットでは、この条件がm->throwing > 0
に変更されています。これは、m->throwing
が単なるブール値ではなく、パニックの深度を示す整数値になったことを示唆しています。
最も重要な変更は、以下の新しい条件文の追加です。
// Special case: always show runtime.panic frame, so that we can
// see where a panic started in the middle of a stack trace.
// See golang.org/issue/5832.
if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
return 1;
このコードスニペットは、runtime·showframe
関数が、現在処理しているフレームの名前が"runtime.panic"
であるかどうかをチェックしています。
name.len == 7+1+5
: これは文字列"runtime.panic"
の長さをチェックしています。"runtime"
が7文字、.
が1文字、"panic"
が5文字で、合計13文字です。このハードコードされた長さチェックは、関数名の正確なマッチングを保証するための最適化または安全策です。hasprefix(name, "runtime.panic")
: これは、フレームの名前が文字列"runtime.panic"
で始まるかどうかをチェックします。
もし現在のフレームがruntime.panic
関数のものであると判断された場合、return 1;
が実行されます。これは、runtime·showframe
関数が1
を返すことで、そのフレームをスタックトレースに含めるべきであることをランタイムに指示します。
この変更により、runtime.panic
フレームは、他の省略される可能性のあるランタイムフレームとは異なり、常にスタックトレースに明示的に表示されるようになります。これにより、パニックがどの時点で、どの関数呼び出しのコンテキストで発生したのかが、デバッグ時に一目でわかるようになります。特に、遅延関数が実行されている最中にスタックトレースを分析する際に、パニックの起点と遅延関数の実行パスを区別する上で非常に役立ちます。
コアとなるコードの変更箇所
変更は、src/pkg/runtime/symtab.c
ファイルのruntime·showframe
関数内で行われています。
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -317,10 +317,17 @@ runtime·showframe(Func *f, G *gp)
static int32 traceback = -1;
String name;
- if(m->throwing && gp != nil && (gp == m->curg || gp == m->caughtsig))
+ if(m->throwing > 0 && gp != nil && (gp == m->curg || gp == m->caughtsig))
return 1;
if(traceback < 0)
traceback = runtime·gotraceback(nil);
name = runtime·gostringnocopy((uint8*)runtime·funcname(f));
+\
+ // Special case: always show runtime.panic frame, so that we can
+ // see where a panic started in the middle of a stack trace.
+ // See golang.org/issue/5832.
+ if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
+ return 1;
+\
return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
}
コアとなるコードの解説
-
if(m->throwing > 0 && gp != nil && (gp == m->curg || gp == m->caughtsig))
:- この行は、Goランタイムの内部状態をチェックしています。
m->throwing
は、現在のM(OSスレッド)がパニック処理中であるかどうかを示すフラグです。以前は単なる真偽値でしたが、このコミットで> 0
というチェックに変わったことから、パニックの深度を示すカウンタになった可能性が高いです。 gp != nil
は、現在のゴルーチンポインタがnil
でないことを確認します。(gp == m->curg || gp == m->caughtsig)
は、現在のゴルーチンが、Mが現在実行中のゴルーチンであるか、またはシグナルによって捕捉されたゴルーチンであるかを確認します。- これらの条件が真の場合、
return 1;
が実行され、現在のフレームはスタックトレースに表示されます。これは、パニック処理中に特定の重要なランタイムフレームを表示するための既存のロジックの一部です。
- この行は、Goランタイムの内部状態をチェックしています。
-
新しい追加ブロック:
// Special case: always show runtime.panic frame, so that we can // see where a panic started in the middle of a stack trace. // See golang.org/issue/5832. if(name.len == 7+1+5 && hasprefix(name, "runtime.panic")) return 1;
- このブロックがこのコミットの主要な変更点です。
- コメントは、このコードの目的を明確に説明しています。「
runtime.panic
フレームを常に表示する特別なケース」であり、「スタックトレースの途中でパニックがどこで始まったかを確認できるようにする」ためであると述べています。また、関連するIssue #5832も参照しています。 name
は、現在処理中のスタックフレームに対応する関数の名前(String
型)です。name.len == 7+1+5
: これは、関数名"runtime.panic"
の正確な長さをチェックしています(runtime
が7文字、.
が1文字、panic
が5文字で合計13文字)。これは、文字列比較の効率化や、意図しない部分文字列マッチを防ぐためのガード条件として機能します。hasprefix(name, "runtime.panic")
: これは、関数名が正確に"runtime.panic"
であるかどうかをチェックします。- これらの条件が両方とも真の場合(つまり、現在のフレームが
runtime.panic
関数のものである場合)、return 1;
が実行されます。これにより、runtime.panic
フレームは、他のフレームの表示条件にかかわらず、常にスタックトレースに含まれることが保証されます。
-
return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
:- これは、
runtime·showframe
関数の元の最後のreturn
文です。 traceback > 1
は、トレースバックレベルが特定の閾値を超えている場合にフレームを表示することを示します。f != nil
は、関数ポインタが有効であることを確認します。contains(name, ".")
は、関数名にドット(.
)が含まれていることをチェックします。これは通常、パッケージ名と関数名を区切るために使用されるため、ユーザー定義の関数やエクスポートされた関数を示唆します。!hasprefix(name, "runtime.")
は、関数名が"runtime."
で始まらないことをチェックします。これは、通常、内部のランタイム関数をスタックトレースから省略するための一般的なルールです。- この最後の
return
文は、runtime.panic
フレームを常に表示するという新しいロジックが適用された後に評価されます。
- これは、
この変更により、Goのデバッグ体験が向上し、パニックの根本原因を特定するプロセスがより効率的になりました。
関連リンク
- Go Issue #5832: https://github.com/golang/go/issues/5832
- Go CL 13302051: https://golang.org/cl/13302051 (このコミットに対応するGoの変更リスト)
参考にした情報源リンク
- Go言語の公式ドキュメント (Panic and Recover): https://go.dev/blog/defer-panic-and-recover
- Goランタイムのソースコード (symtab.c): https://github.com/golang/go/blob/master/src/runtime/symtab.go (Go 1.1以降は
symtab.go
に移行しているが、当時のCコードのロジックを理解するために参照) - Goのスタックトレースに関する議論やドキュメント (一般的な情報源)
- GoのIssueトラッカー (Issue #5832の詳細)
- Goのコードレビューシステム (CL 13302051の詳細)
[インデックス 17556] ファイルの概要
このコミットは、Goランタイムのスタックトレース表示に関する改善です。具体的には、パニック発生時にruntime.panic
フレームがスタックトレースに常に表示されるように変更することで、デバッグ時の情報量を増やし、パニックの発生源をより明確に理解できるようにすることを目的としています。
コミット
commit fa4984d535b23c0d2b14650a8842d63083893af3
Author: Russ Cox <rsc@golang.org>
Date: Wed Sep 11 11:59:19 2013 -0400
runtime: show runtime.panic frame in traceback
Otherwise, if panic starts running deferred functions,
the code that panicked appears to be calling those
functions directly, which is not the case and can be
confusing.
For example:
main.Two()
/Users/rsc/x.go:12 +0x2a
runtime.panic(0x20dc0, 0x2100cc010)
/Users/rsc/g/go/src/pkg/runtime/panic.c:248 +0x106
main.One()
/Users/rsc/x.go:8 +0x55
This makes clear(er) that main.Two is being called during
a panic, not as a direct call from main.One.
Fixes #5832.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13302051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fa4984d535b23c0d2b14650a8842d63083893af3
元コミット内容
このコミットの元の内容は、Goランタイムが生成するスタックトレースにおいて、runtime.panic
関数呼び出しのフレームを常に表示するように変更することです。これにより、パニックが発生し、遅延関数(deferred functions)が実行される際に、スタックトレースが誤解を招くような表示になる問題を解決します。
具体的には、パニックが遅延関数を実行し始めると、パニックを引き起こしたコードが、あたかもそれらの遅延関数を直接呼び出しているかのように見えてしまうことがありました。これは実際にはそうではないため、混乱を招く可能性がありました。
コミットメッセージの例では、main.Two()
がパニックを引き起こし、その後にruntime.panic
が呼ばれ、さらにmain.One()
がスタックトレースに現れるケースが示されています。この変更により、runtime.panic
フレームが明示的に表示されることで、main.Two
がパニック中に呼び出されているのであって、main.One
からの直接の呼び出しではないことがより明確になります。
この変更は、GoのIssue #5832を修正するものです。
変更の背景
Go言語では、プログラムの実行中に予期せぬエラーが発生した場合、panic
メカニズムが使用されます。panic
が発生すると、通常のプログラムフローは中断され、現在のゴルーチン(goroutine)の遅延関数が実行され始めます。その後、スタックが巻き戻され(unwind)、最終的にプログラムがクラッシュするか、recover
関数によってパニックが捕捉されない限り、プログラムは終了します。
このパニック処理の過程で、スタックトレースが生成され、開発者は問題の原因を特定するための重要な情報を得ることができます。しかし、このコミットが導入される以前は、runtime.panic
関数自体がスタックトレースから省略されることがありました。
この省略は、特に遅延関数が複雑な処理を行う場合や、複数の関数呼び出しが絡む場合に、スタックトレースの解釈を困難にしていました。パニックを引き起こした真の関数が、あたかも遅延関数を直接呼び出しているかのように見えてしまうため、デバッグ時に誤った推論をしてしまう可能性があったのです。
Issue #5832では、この問題が具体的に報告されており、runtime.panic
フレームの欠落がスタックトレースの可読性を損ね、デバッグ体験を悪化させていることが指摘されていました。このコミットは、このデバッグ上の課題を解決し、より正確で理解しやすいスタックトレースを提供することを目的としています。
前提知識の解説
Go言語のパニックとリカバリー (Panic and Recover)
- Panic: Go言語におけるランタイムエラーの一種で、プログラムの異常終了を引き起こします。
panic
関数を明示的に呼び出すこともできますし、ゼロポインタ参照や配列の範囲外アクセスなどのランタイムエラーによって暗黙的に発生することもあります。panic
が発生すると、現在の関数の実行は即座に停止し、その関数に登録されているdefer
関数が実行されます。その後、呼び出し元の関数へとスタックが巻き戻され、各関数のdefer
関数が順に実行されます。このプロセスがゴルーチンのスタックの最上位まで続くと、プログラムはクラッシュします。 - Defer:
defer
文は、その関数がリターンする直前(またはパニックによってスタックが巻き戻される直前)に実行される関数を登録するために使用されます。リソースの解放(ファイルのクローズ、ロックの解除など)や、パニックからの回復(recover
の使用)によく利用されます。 - Recover:
recover
関数は、defer
関数内で呼び出された場合にのみ有効です。recover
が呼び出されると、現在のゴルーチンで発生しているパニックを捕捉し、パニックの値を返します。これにより、パニックによるプログラムのクラッシュを防ぎ、通常の実行フローを再開させることができます。recover
がdefer
関数以外で呼び出された場合、またはパニックが発生していないときに呼び出された場合、nil
を返します。
スタックトレース (Stack Trace)
スタックトレースは、プログラムの実行中に特定の時点(通常はエラーやパニックが発生した時点)で、現在実行中の関数とその呼び出し元の関数がどのように連鎖しているかを示すリストです。各エントリ(フレーム)は、関数名、ファイル名、行番号、およびプログラムカウンタ(PC)オフセットなどの情報を含みます。スタックトレースは、問題の根本原因を特定し、プログラムの実行パスを理解するために不可欠なデバッグツールです。
Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理するシステムです。これには、ガベージコレクタ、スケジューラ(ゴルーチンの管理)、メモリ管理、プリミティブな同期メカニズム、そしてパニック処理などが含まれます。Goプログラムがコンパイルされると、ランタイムのコードが実行可能バイナリにリンクされます。このコミットで変更されているsrc/pkg/runtime/symtab.c
は、Goランタイムの一部であり、スタックトレースのシンボル解決や表示ロジックに関わるC言語のソースファイルです。
m->throwing
(Goランタイム内部変数)
Goランタイムの内部では、m
はM(Machine)構造体を指し、OSのスレッドを表します。m->throwing
は、このMが現在パニック処理中であるかどうかを示すフラグです。このコミットでは、m->throwing
が単なる真偽値ではなく、パニックの深度を示すカウンタとして扱われるようになったため、m->throwing > 0
というチェックに変更されています。これは、複数のパニックが同時に発生するような稀なケース(例えば、defer
関数内でさらにパニックが発生するような場合)に対応するため、または単に内部的な表現の変更によるものです。
技術的詳細
このコミットの技術的な核心は、Goランタイムのスタックトレース生成ロジック、特にruntime·showframe
関数の変更にあります。
runtime·showframe
関数は、スタックトレースを生成する際に、特定のフレームを表示するかどうかを決定する役割を担っています。Goのスタックトレースは、デバッグの関連性を高めるために、一部の内部ランタイム関数フレームを省略することがあります。しかし、runtime.panic
フレームの省略は、パニックの発生源を曖昧にするという副作用がありました。
変更前は、m->throwing
が真(パニック処理中)の場合、特定のフレーム(おそらくはパニック処理に関連する内部フレーム)を表示しないロジックがありました。このコミットでは、この条件がm->throwing > 0
に変更されています。これは、m->throwing
が単なるブール値ではなく、パニックの深度を示す整数値になったことを示唆しています。
最も重要な変更は、以下の新しい条件文の追加です。
// Special case: always show runtime.panic frame, so that we can
// see where a panic started in the middle of a stack trace.
// See golang.org/issue/5832.
if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
return 1;
このコードスニペットは、runtime·showframe
関数が、現在処理しているフレームの名前が"runtime.panic"
であるかどうかをチェックしています。
name.len == 7+1+5
: これは文字列"runtime.panic"
の長さをチェックしています。"runtime"
が7文字、.
が1文字、"panic"
が5文字で、合計13文字です。このハードコードされた長さチェックは、関数名の正確なマッチングを保証するための最適化または安全策です。hasprefix(name, "runtime.panic")
: これは、フレームの名前が文字列"runtime.panic"
で始まるかどうかをチェックします。
もし現在のフレームがruntime.panic
関数のものであると判断された場合、return 1;
が実行されます。これは、runtime·showframe
関数が1
を返すことで、そのフレームをスタックトレースに含めるべきであることをランタイムに指示します。
この変更により、runtime.panic
フレームは、他の省略される可能性のあるランタイムフレームとは異なり、常にスタックトレースに明示的に表示されるようになります。これにより、パニックがどの時点で、どの関数呼び出しのコンテキストで発生したのかが、デバッグ時に一目でわかるようになります。特に、遅延関数が実行されている最中にスタックトレースを分析する際に、パニックの起点と遅延関数の実行パスを区別する上で非常に役立ちます。
コアとなるコードの変更箇所
変更は、src/pkg/runtime/symtab.c
ファイルのruntime·showframe
関数内で行われています。
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -317,10 +317,17 @@ runtime·showframe(Func *f, G *gp)
static int32 traceback = -1;
String name;
- if(m->throwing && gp != nil && (gp == m->curg || gp == m->caughtsig))
+ if(m->throwing > 0 && gp != nil && (gp == m->curg || gp == m->caughtsig))
return 1;
if(traceback < 0)
traceback = runtime·gotraceback(nil);
name = runtime·gostringnocopy((uint8*)runtime·funcname(f));
+\
+ // Special case: always show runtime.panic frame, so that we can
+ // see where a panic started in the middle of a stack trace.
+ // See golang.org/issue/5832.
+ if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
+ return 1;
+\
return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
}
コアとなるコードの解説
-
if(m->throwing > 0 && gp != nil && (gp == m->curg || gp == m->caughtsig))
:- この行は、Goランタイムの内部状態をチェックしています。
m->throwing
は、現在のM(OSスレッド)がパニック処理中であるかどうかを示すフラグです。以前は単なる真偽値でしたが、このコミットで> 0
というチェックに変わったことから、パニックの深度を示すカウンタになった可能性が高いです。 gp != nil
は、現在のゴルーチンポインタがnil
でないことを確認します。(gp == m->curg || gp == m->caughtsig)
は、現在のゴルーチンが、Mが現在実行中のゴルーチンであるか、またはシグナルによって捕捉されたゴルーチンであるかを確認します。- これらの条件が真の場合、
return 1;
が実行され、現在のフレームはスタックトレースに表示されます。これは、パニック処理中に特定の重要なランタイムフレームを表示するための既存のロジックの一部です。
- この行は、Goランタイムの内部状態をチェックしています。
-
新しい追加ブロック:
// Special case: always show runtime.panic frame, so that we can // see where a panic started in the middle of a stack trace. // See golang.org/issue/5832. if(name.len == 7+1+5 && hasprefix(name, "runtime.panic")) return 1;
- このブロックがこのコミットの主要な変更点です。
- コメントは、このコードの目的を明確に説明しています。「
runtime.panic
フレームを常に表示する特別なケース」であり、「スタックトレースの途中でパニックがどこで始まったかを確認できるようにする」ためであると述べています。また、関連するIssue #5832も参照しています。 name
は、現在処理中のスタックフレームに対応する関数の名前(String
型)です。name.len == 7+1+5
: これは、関数名"runtime.panic"
の正確な長さをチェックしています(runtime
が7文字、.
が1文字、panic
が5文字で合計13文字)。これは、文字列比較の効率化や、意図しない部分文字列マッチを防ぐためのガード条件として機能します。hasprefix(name, "runtime.panic")
: これは、関数名が正確に"runtime.panic"
であるかどうかをチェックします。- これらの条件が両方とも真の場合(つまり、現在のフレームが
runtime.panic
関数のものである場合)、return 1;
が実行されます。これにより、runtime.panic
フレームは、他のフレームの表示条件にかかわらず、常にスタックトレースに含まれることが保証されます。
-
return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
:- これは、
runtime·showframe
関数の元の最後のreturn
文です。 traceback > 1
は、トレースバックレベルが特定の閾値を超えている場合にフレームを表示することを示します。f != nil
は、関数ポインタが有効であることを確認します。contains(name, ".")
は、関数名にドット(.
)が含まれていることをチェックします。これは通常、パッケージ名と関数名を区切るために使用されるため、ユーザー定義の関数やエクスポートされた関数を示唆します。!hasprefix(name, "runtime.")
は、関数名が"runtime."
で始まらないことをチェックします。これは、通常、内部のランタイム関数をスタックトレースから省略するための一般的なルールです。- この最後の
return
文は、runtime.panic
フレームを常に表示するという新しいロジックが適用された後に評価されます。
- これは、
この変更により、Goのデバッグ体験が向上し、パニックの根本原因を特定するプロセスがより効率的になりました。
関連リンク
- Go CL 13302051: https://golang.org/cl/13302051 (このコミットに対応するGoの変更リスト)
参考にした情報源リンク
- Go言語の公式ドキュメント (Panic and Recover): https://go.dev/blog/defer-panic-and-recover
- Goランタイムのソースコード (symtab.c): https://github.com/golang/go/blob/master/src/runtime/symtab.go (Go 1.1以降は
symtab.go
に移行しているが、当時のCコードのロジックを理解するために参照) - Goのスタックトレースに関する議論やドキュメント (一般的な情報源)
- Goのコードレビューシステム (CL 13302051の詳細)