[インデックス 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
の一般的な理解。