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

[インデックス 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=0Callers関数自身を指し、その呼び出し元は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点です。

  1. skipパラメータの説明が「skipが0の場合、Callerの呼び出し元を識別する」から「skipが1の場合、Callerの呼び出し元を識別する」に変更されています。これは、Callers関数との整合性を図るための記述変更であり、実際のCaller関数のskip=0の挙動は変わっていません。この変更は、Callers関数との比較において、Callerskipの解釈が「1」から始まるという視点を提供しようとしていると解釈できます。
  2. (For historical reasons the meaning of skip differs between Caller and Callers.) という一文が追加されました。これは、CallerCallersの間で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パラメータの解釈に関する説明を修正し、さらにCallerCallersの間でskipの意味が異なるという「歴史的な理由」による不一致があることを追記しています。

このコミットは、Goのランタイムコードの動作自体には一切影響を与えません。純粋にドキュメンテーションの正確性を向上させ、APIの挙動に関する潜在的な混乱を解消するためのものです。

関連リンク

参考にした情報源リンク

  • 上記の「関連リンク」セクションに記載されているGo言語の公式ドキュメンテーション。
  • コミットメッセージに記載されているGoの変更リスト (CL)。
  • Go言語のソースコード(src/pkg/runtime/extern.go)。
  • Go言語におけるスタックトレースとruntime.Caller/runtime.Callersの一般的な理解。