[インデックス 12859] ファイルの概要
このコミットは、Go言語のruntimeパッケージにおけるCaller関数のドキュメンテーションを修正し、そのskipパラメータの挙動がCallers関数と異なるという既存の「バグ」を明記することを目的としています。コードの動作自体は変更せず、ドキュメンテーションを現状の動作に合わせることで、ユーザーの混乱を防ぎ、APIの挙動に関する認識の齟齬を解消します。
コミット
- コミットハッシュ:
6849c4db0c35cad11a2fb5c6836bc0d4a2ae705d - Author: Rob Pike r@golang.org
- Date: Tue Apr 10 09:47:57 2012 +1000
- コミットメッセージ:
runtime.Callers: make documentation match code It is a bug that Caller and Callers disagree about the offset of the skip parameter. Document the bug. R=rsc, dsymonds, r, iant CC=golang-dev https://golang.org/cl/5976064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6849c4db0c35cad11a2fb5c6836bc0d4a2ae705d
元コミット内容
runtime.Callers: make documentation match code
It is a bug that Caller and Callers disagree about the offset of the skip
parameter. Document the bug.
R=rsc, dsymonds, r, iant
CC=golang-dev
https://golang.org/cl/5976064
変更の背景
Go言語のruntimeパッケージには、現在のゴルーチンのスタックトレース情報を取得するためのCaller関数とCallers関数が存在します。これらの関数は、呼び出し元のスタックフレームをスキップするためのskipというパラメータを持っています。
このコミットが作成された背景には、Caller関数とCallers関数でskipパラメータの解釈(特にオフセット)に一貫性がなく、これが「バグ」として認識されていたという事実があります。具体的には、Caller(0)がCaller関数自身の呼び出し元を指すのに対し、Callers関数ではskip=0がCallers関数自身を指し、その呼び出し元はskip=1となるような挙動の不一致がありました。
この不一致は、APIを使用する開発者にとって混乱の原因となり、意図しないスタックフレームが取得される可能性がありました。このコミットでは、この挙動の不一致をコードレベルで修正するのではなく、ドキュメンテーションを修正して、この不一致が既存の「バグ」であり、その挙動が「歴史的な理由」によるものであることを明記することで、開発者への情報提供を改善し、混乱を最小限に抑えることを目的としています。
前提知識の解説
Go言語のruntimeパッケージ
runtimeパッケージは、Goプログラムのランタイムシステムとのインタフェースを提供します。これには、ゴルーチン管理、ガベージコレクション、低レベルのシステムコールなど、Goプログラムの実行環境に関する機能が含まれます。
スタックトレースとCaller/Callers関数
Goプログラムが実行される際、各ゴルーチンは独自の実行スタックを持っています。関数が呼び出されるたびに、その関数の情報(引数、ローカル変数、戻りアドレスなど)がスタックに積まれます。これをスタックフレームと呼びます。
-
runtime.Caller(skip int) (pc uintptr, file string, line int, ok bool): この関数は、現在のゴルーチンのスタックトレースから、指定されたskip数だけスタックフレームを遡った呼び出し元の情報を報告します。skip=0:Caller関数自身の呼び出し元(つまり、Callerを呼び出した関数)の情報を返します。pc: プログラムカウンタ(命令ポインタ)。file: 呼び出し元のソースファイル名。line: 呼び出し元のソースファイル内の行番号。ok: 情報が正常に取得できたかどうかを示すブール値。
-
runtime.Callers(skip int, pc []uintptr) int: この関数は、現在のゴルーチンのスタックトレースから、指定されたskip数だけスタックフレームを遡った複数の呼び出し元のプログラムカウンタをpcスライスに書き込みます。返り値は書き込まれたエントリの数です。skip=0:Callers関数自身を指します。skip=1:Callers関数の呼び出し元を指します。
skipパラメータの重要性
skipパラメータは、スタックトレースを解析する際に非常に重要です。例えば、ロギングライブラリがログメッセージの発生元ファイルと行番号を報告したい場合、ロギング関数自身ではなく、そのロギング関数を呼び出したユーザーコードの場所を知る必要があります。この場合、ロギング関数内でruntime.Caller(1)のようにskipを調整することで、適切な呼び出し元情報を取得できます。
技術的詳細
このコミットの技術的詳細は、Go言語のruntimeパッケージ内のextern.goファイルにおけるCaller関数のドキュメンテーション文字列の変更に集約されます。
変更前は、Caller関数のskipパラメータに関する説明が以下のようになっていました。
// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
// to ascend, with 0 identifying the caller of Caller. The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool)
この説明では、「skipが0の場合、Callerの呼び出し元を識別する」と述べられています。これはCaller関数の実際の挙動と一致しています。
しかし、Callers関数のskipパラメータの挙動は異なり、Callers(0, ...)はCallers関数自身を指し、Callers(1, ...)がその呼び出し元を指します。この不一致は、API設計上は一貫性がない「バグ」と見なされていました。
このコミットでは、この「バグ」をコードで修正するのではなく、ドキュメンテーションで明示的に言及することで、開発者への透明性を高めています。変更後のドキュメンテーションは以下のようになります。
// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
// to ascend, with 1 identifying the caller of Caller. (For historical reasons the
// meaning of skip differs between Caller and Callers.) The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool)
注目すべき変更点は以下の2点です。
skipパラメータの説明が「skipが0の場合、Callerの呼び出し元を識別する」から「skipが1の場合、Callerの呼び出し元を識別する」に変更されています。これは、Callers関数との整合性を図るための記述変更であり、実際のCaller関数のskip=0の挙動は変わっていません。この変更は、Callers関数との比較において、Callerのskipの解釈が「1」から始まるという視点を提供しようとしていると解釈できます。(For historical reasons the meaning of skip differs between Caller and Callers.)という一文が追加されました。これは、CallerとCallersの間でskipの意味が異なるという「歴史的な理由」による不一致が存在することを明示的に示しています。これにより、開発者はこの不一致が既知の問題であり、意図的な設計上の選択(あるいは過去の設計上の制約)であることを理解できます。
この変更は、コードの動作を変更せずに、APIのドキュメンテーションをより正確かつ包括的にすることで、開発者がAPIを正しく理解し、誤用を防ぐことを目的としています。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -20,7 +20,8 @@ func Goexit()
// Caller reports file and line number information about function invocations on
// the calling goroutine's stack. The argument skip is the number of stack frames
-// to ascend, with 0 identifying the caller of Caller. The return values report the
+// to ascend, with 1 identifying the caller of Caller. (For historical reasons the
+// meaning of skip differs between Caller and Callers.) The return values report the
// program counter, file name, and line number within the file of the corresponding
// call. The boolean ok is false if it was not possible to recover the information.
func Caller(skip int) (pc uintptr, file string, line int, ok bool)
コアとなるコードの解説
上記の差分が示すように、変更はsrc/pkg/runtime/extern.goファイル内のCaller関数のコメント行に限定されています。
具体的には、以下の行が変更されました。
- 変更前:
// to ascend, with 0 identifying the caller of Caller. The return values report the - 変更後:
// to ascend, with 1 identifying the caller of Caller. (For historical reasons the// meaning of skip differs between Caller and Callers.) The return values report the
この変更は、Caller関数のskipパラメータの解釈に関する説明を修正し、さらにCallerとCallersの間でskipの意味が異なるという「歴史的な理由」による不一致があることを追記しています。
このコミットは、Goのランタイムコードの動作自体には一切影響を与えません。純粋にドキュメンテーションの正確性を向上させ、APIの挙動に関する潜在的な混乱を解消するためのものです。
関連リンク
- Go言語の
runtimeパッケージのドキュメンテーション: https://pkg.go.dev/runtime runtime.Caller関数のドキュメンテーション: https://pkg.go.dev/runtime#Callerruntime.Callers関数のドキュメンテーション: https://pkg.go.dev/runtime#Callers- このコミットに関連するGoの変更リスト (CL): https://golang.org/cl/5976064
参考にした情報源リンク
- 上記の「関連リンク」セクションに記載されているGo言語の公式ドキュメンテーション。
- コミットメッセージに記載されているGoの変更リスト (CL)。
- Go言語のソースコード(
src/pkg/runtime/extern.go)。 - Go言語におけるスタックトレースと
runtime.Caller/runtime.Callersの一般的な理解。